diff --git a/examples/device_scanner.py b/examples/device_scanner.py new file mode 100644 index 0000000..24e7682 --- /dev/null +++ b/examples/device_scanner.py @@ -0,0 +1,25 @@ +import asyncio +import logging + +from findmy.scanner import OfflineFindingScanner + +logging.basicConfig(level=logging.INFO) + + +async def scan(): + scanner = await OfflineFindingScanner.create() + + print("Scanning for FindMy-devices...") + print() + + async for device in scanner.scan_for(10, extend_timeout=True): + print(f"Device - {device.mac_address}") + print(f" Public key: {device.adv_key_b64}") + print(f" Lookup key: {device.hashed_adv_key_b64}") + print(f" Status byte: {device.status:x}") + print(f" Hint byte: {device.hint:x}") + print() + + +if __name__ == "__main__": + asyncio.run(scan()) diff --git a/findmy/__init__.py b/findmy/__init__.py index 3701cdb..cffa8b1 100644 --- a/findmy/__init__.py +++ b/findmy/__init__.py @@ -1,5 +1,5 @@ """A package providing everything you need to work with Apple's FindMy network.""" -from . import reports +from . import keys, reports from .util import errors -__all__ = ("reports", "errors") +__all__ = ("reports", "keys", "errors") diff --git a/findmy/reports/keys.py b/findmy/keys.py similarity index 85% rename from findmy/reports/keys.py rename to findmy/keys.py index f806b68..bb26441 100644 --- a/findmy/reports/keys.py +++ b/findmy/keys.py @@ -3,12 +3,42 @@ import base64 import hashlib import secrets +from abc import ABC, abstractmethod from cryptography.hazmat.backends import default_backend from cryptography.hazmat.primitives.asymmetric import ec -class KeyPair: +class HasPublicKey(ABC): + """ + ABC for anything that has a public FindMy-key. + + Also called an "advertisement" key, since it is the key that is advertised by findable devices. + """ + + @property + @abstractmethod + def adv_key_bytes(self) -> bytes: + """Return the advertised (public) key as bytes.""" + raise NotImplementedError + + @property + def adv_key_b64(self) -> str: + """Return the advertised (public) key as a base64-encoded string.""" + return base64.b64encode(self.adv_key_bytes).decode("ascii") + + @property + def hashed_adv_key_bytes(self) -> bytes: + """Return the hashed advertised (public) key as bytes.""" + return hashlib.sha256(self.adv_key_bytes).digest() + + @property + def hashed_adv_key_b64(self) -> str: + """Return the hashed advertised (public) key as a base64-encoded string.""" + return base64.b64encode(self.hashed_adv_key_bytes).decode("ascii") + + +class KeyPair(HasPublicKey): """A private-public keypair for a trackable FindMy accessory.""" def __init__(self, private_key: bytes) -> None: @@ -55,21 +85,6 @@ class KeyPair: key_bytes = self._priv_key.public_key().public_numbers().x return int.to_bytes(key_bytes, 28, "big") - @property - def adv_key_b64(self) -> str: - """Return the advertised (public) key as a base64-encoded string.""" - return base64.b64encode(self.adv_key_bytes).decode("ascii") - - @property - def hashed_adv_key_bytes(self) -> bytes: - """Return the hashed advertised (public) key as bytes.""" - return hashlib.sha256(self.adv_key_bytes).digest() - - @property - def hashed_adv_key_b64(self) -> str: - """Return the hashed advertised (public) key as a base64-encoded string.""" - return base64.b64encode(self.hashed_adv_key_bytes).decode("ascii") - def dh_exchange(self, other_pub_key: ec.EllipticCurvePublicKey) -> bytes: """Do a Diffie-Hellman key exchange using another EC public key.""" return self._priv_key.exchange(ec.ECDH(), other_pub_key) diff --git a/findmy/reports/__init__.py b/findmy/reports/__init__.py index 50bf138..b358a97 100644 --- a/findmy/reports/__init__.py +++ b/findmy/reports/__init__.py @@ -1,7 +1,6 @@ """Code related to fetching location reports.""" from .account import AppleAccount, AsyncAppleAccount from .anisette import RemoteAnisetteProvider -from .keys import KeyPair from .state import LoginState from .twofactor import SmsSecondFactorMethod @@ -10,6 +9,5 @@ __all__ = ( "AsyncAppleAccount", "LoginState", "RemoteAnisetteProvider", - "KeyPair", "SmsSecondFactorMethod", ) diff --git a/findmy/reports/account.py b/findmy/reports/account.py index 832ec9b..168a494 100644 --- a/findmy/reports/account.py +++ b/findmy/reports/account.py @@ -38,8 +38,9 @@ from .twofactor import ( ) if TYPE_CHECKING: + from findmy.keys import KeyPair + from .anisette import BaseAnisetteProvider - from .keys import KeyPair logging.getLogger(__name__) diff --git a/findmy/reports/reports.py b/findmy/reports/reports.py index 93da46f..b85b54b 100644 --- a/findmy/reports/reports.py +++ b/findmy/reports/reports.py @@ -14,7 +14,7 @@ from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes from findmy.util import HttpSession if TYPE_CHECKING: - from .keys import KeyPair + from findmy.keys import KeyPair _session = HttpSession() diff --git a/findmy/scanner/__init__.py b/findmy/scanner/__init__.py new file mode 100644 index 0000000..9336c6e --- /dev/null +++ b/findmy/scanner/__init__.py @@ -0,0 +1,4 @@ +"""Utilities related to physically discoverable FindMy-devices.""" +from .scanner import OfflineFindingScanner + +__all__ = ("OfflineFindingScanner",) diff --git a/findmy/scanner/scanner.py b/findmy/scanner/scanner.py new file mode 100644 index 0000000..a192859 --- /dev/null +++ b/findmy/scanner/scanner.py @@ -0,0 +1,205 @@ +"""Airtag scanner.""" +from __future__ import annotations + +import asyncio +import logging +import time +from typing import AsyncGenerator + +import bleak + +from findmy.keys import HasPublicKey + +logging.getLogger(__name__) + + +class OfflineFindingDevice(HasPublicKey): + """Device discoverable through Apple's bluetooth-based Offline Finding protocol.""" + + OF_HEADER_SIZE = 2 + OF_TYPE = 0x12 + OF_DATA_LEN = 25 + + def __init__( + self, + mac_bytes: bytes, + status: int, + public_key: bytes, + hint: int, + ) -> None: + """Initialize an `OfflineFindingDevice`.""" + self._mac_bytes: bytes = mac_bytes + self._status: int = status + self._public_key: bytes = public_key + self._hint: int = hint + + @property + def mac_address(self) -> str: + """MAC address of the device in AA:BB:CC:DD:EE:FF format.""" + mac = self._mac_bytes.hex().upper() + return ":".join(mac[i : i + 2] for i in range(0, len(mac), 2)) + + @property + def status(self) -> int: + """Status value as reported by the device.""" + return self._status % 255 + + @property + def hint(self) -> int: + """Hint value as reported by the device.""" + return self._hint % 255 + + @property + def adv_key_bytes(self) -> bytes: + """See `HasPublicKey.adv_key_bytes`.""" + return self._public_key + + @classmethod + def from_payload( + cls, + mac_address: str, + payload: bytes, + ) -> OfflineFindingDevice | None: + """Get an OfflineFindingDevice object from a BLE payload.""" + if len(payload) < cls.OF_HEADER_SIZE: + logging.error("Not enough bytes to decode: %s", len(payload)) + return None + if payload[0] != cls.OF_TYPE: + logging.debug("Unsupported OF type: %s", payload[0]) + return None + if payload[1] != cls.OF_DATA_LEN: + logging.debug("Unknown OF data length: %s", payload[1]) + return None + if len(payload) != cls.OF_HEADER_SIZE + cls.OF_DATA_LEN: + logging.debug( + "Invalid OF data length: %s instead of %s", + len(payload) - cls.OF_HEADER_SIZE, + payload[1], + ) + return None + + mac_bytes = bytes.fromhex(mac_address.replace(":", "").replace("-", "")) + + status = payload[cls.OF_HEADER_SIZE + 0] + + pubkey_end = payload[cls.OF_HEADER_SIZE + 1 : cls.OF_HEADER_SIZE + 23] + pubkey_middle = mac_bytes[1:] + pubkey_start_ms = payload[cls.OF_HEADER_SIZE + 23] << 6 + pubkey_start_ls = mac_bytes[0] & 0b00111111 + pubkey_start = (pubkey_start_ms | pubkey_start_ls).to_bytes(1, "big") + pubkey = pubkey_start + pubkey_middle + pubkey_end + + hint = payload[cls.OF_HEADER_SIZE + 24] + + return OfflineFindingDevice(mac_bytes, status, pubkey, hint) + + def __repr__(self) -> str: + """Human-readable string representation of an OfflineFindingDevice.""" + return ( + f"OfflineFindingDevice({self.mac_address}, pubkey={self.adv_key_b64}," + f" status={self.status}, hint={self.hint})" + ) + + def __eq__(self, other: OfflineFindingDevice) -> bool: + """Check if two OfflineFindingDevices are equal by comparing their MAC addresses.""" + if not isinstance(other, OfflineFindingDevice): + return False + return other.mac_address == self.mac_address + + def __hash__(self) -> int: + """Hash an OfflineFindingDevice. This is simply the MAC address as an integer.""" + return int.from_bytes(self._mac_bytes, "big") + + +class OfflineFindingScanner: + """BLE scanner that searches for `OfflineFindingDevice`s.""" + + _scan_ctrl_lock = asyncio.Lock() + + BLE_COMPANY_APPLE = 0x004C + + def __init__(self, loop: asyncio.AbstractEventLoop) -> None: + """ + Initialize an instance of the Scanner using an event loop. + + You most likely do not want to use this yourself; + check out `OfflineFindingScanner.create` instead. + """ + self._scanner: bleak.BleakScanner = bleak.BleakScanner(self._scan_callback) + + self._loop = loop + self._device_fut: asyncio.Future[ + (bleak.BLEDevice, bleak.AdvertisementData) + ] = loop.create_future() + + self._scanner_count: int = 0 + + @classmethod + async def create(cls) -> OfflineFindingScanner: + """Create an instance of the scanner.""" + loop = asyncio.get_running_loop() + return cls(loop) + + async def _start_scan(self) -> None: + async with self._scan_ctrl_lock: + if self._scanner_count == 0: + logging.info("Starting BLE scanner") + await self._scanner.start() + self._scanner_count += 1 + + async def _stop_scan(self) -> None: + async with self._scan_ctrl_lock: + self._scanner_count -= 1 + if self._scanner_count == 0: + logging.info("Stopping BLE scanner") + await self._scanner.stop() + + async def _scan_callback( + self, + device: bleak.BLEDevice, + data: bleak.AdvertisementData, + ) -> None: + self._device_fut.set_result((device, data)) + self._device_fut = self._loop.create_future() + + async def _wait_for_device(self, timeout: float) -> OfflineFindingDevice | None: + device, data = await asyncio.wait_for(self._device_fut, timeout=timeout) + + apple_data = data.manufacturer_data.get(self.BLE_COMPANY_APPLE, b"") + if not apple_data: + return None + + return OfflineFindingDevice.from_payload(device.address, apple_data) + + async def scan_for( + self, + timeout: float = 10, + *, + extend_timeout: bool = False, + ) -> AsyncGenerator[OfflineFindingDevice]: + """ + Scan for `OfflineFindingDevice`s for up to `timeout` seconds. + + If `extend_timeout` is set, the timer will be extended + by `timeout` seconds every time a new device is discovered. + """ + await self._start_scan() + + stop_at = time.time() + timeout + devices_seen: set[OfflineFindingDevice] = set() + + try: + time_left = stop_at - time.time() + while time_left > 0: + device = await self._wait_for_device(time_left) + if device is not None and device not in devices_seen: + devices_seen.add(device) + if extend_timeout: + stop_at = time.time() + timeout + yield device + + time_left = stop_at - time.time() + except (asyncio.CancelledError, asyncio.TimeoutError): # timeout reached + return + finally: + await self._stop_scan() diff --git a/poetry.lock b/poetry.lock index 328a041..a68dcdd 100644 --- a/poetry.lock +++ b/poetry.lock @@ -217,6 +217,55 @@ soupsieve = ">1.2" html5lib = ["html5lib"] lxml = ["lxml"] +[[package]] +name = "bleak" +version = "0.21.1" +description = "Bluetooth Low Energy platform Agnostic Klient" +category = "main" +optional = true +python-versions = ">=3.8,<3.13" +files = [ + {file = "bleak-0.21.1-py3-none-any.whl", hash = "sha256:ccec260a0f5ec02dd133d68b0351c0151b2ecf3ddd0bcabc4c04a1cdd7f33256"}, + {file = "bleak-0.21.1.tar.gz", hash = "sha256:ec4a1a2772fb315b992cbaa1153070c7e26968a52b0e2727035f443a1af5c18f"}, +] + +[package.dependencies] +async-timeout = {version = ">=3.0.0,<5", markers = "python_version < \"3.11\""} +bleak-winrt = {version = ">=1.2.0,<2.0.0", markers = "platform_system == \"Windows\" and python_version < \"3.12\""} +dbus-fast = {version = ">=1.83.0,<3", markers = "platform_system == \"Linux\""} +pyobjc-core = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} +pyobjc-framework-CoreBluetooth = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} +pyobjc-framework-libdispatch = {version = ">=9.2,<10.0", markers = "platform_system == \"Darwin\""} +typing-extensions = {version = ">=4.7.0", markers = "python_version < \"3.12\""} +"winrt-Windows.Devices.Bluetooth" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Devices.Bluetooth.Advertisement" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Devices.Bluetooth.GenericAttributeProfile" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Devices.Enumeration" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Foundation" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Foundation.Collections" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} +"winrt-Windows.Storage.Streams" = {version = "2.0.0b1", markers = "platform_system == \"Windows\" and python_version >= \"3.12\""} + +[[package]] +name = "bleak-winrt" +version = "1.2.0" +description = "Python WinRT bindings for Bleak" +category = "main" +optional = true +python-versions = "*" +files = [ + {file = "bleak-winrt-1.2.0.tar.gz", hash = "sha256:0577d070251b9354fc6c45ffac57e39341ebb08ead014b1bdbd43e211d2ce1d6"}, + {file = "bleak_winrt-1.2.0-cp310-cp310-win32.whl", hash = "sha256:a2ae3054d6843ae0cfd3b94c83293a1dfd5804393977dd69bde91cb5099fc47c"}, + {file = "bleak_winrt-1.2.0-cp310-cp310-win_amd64.whl", hash = "sha256:677df51dc825c6657b3ae94f00bd09b8ab88422b40d6a7bdbf7972a63bc44e9a"}, + {file = "bleak_winrt-1.2.0-cp311-cp311-win32.whl", hash = "sha256:9449cdb942f22c9892bc1ada99e2ccce9bea8a8af1493e81fefb6de2cb3a7b80"}, + {file = "bleak_winrt-1.2.0-cp311-cp311-win_amd64.whl", hash = "sha256:98c1b5a6a6c431ac7f76aa4285b752fe14a1c626bd8a1dfa56f66173ff120bee"}, + {file = "bleak_winrt-1.2.0-cp37-cp37m-win32.whl", hash = "sha256:623ac511696e1f58d83cb9c431e32f613395f2199b3db7f125a3d872cab968a4"}, + {file = "bleak_winrt-1.2.0-cp37-cp37m-win_amd64.whl", hash = "sha256:13ab06dec55469cf51a2c187be7b630a7a2922e1ea9ac1998135974a7239b1e3"}, + {file = "bleak_winrt-1.2.0-cp38-cp38-win32.whl", hash = "sha256:5a36ff8cd53068c01a795a75d2c13054ddc5f99ce6de62c1a97cd343fc4d0727"}, + {file = "bleak_winrt-1.2.0-cp38-cp38-win_amd64.whl", hash = "sha256:810c00726653a962256b7acd8edf81ab9e4a3c66e936a342ce4aec7dbd3a7263"}, + {file = "bleak_winrt-1.2.0-cp39-cp39-win32.whl", hash = "sha256:dd740047a08925bde54bec357391fcee595d7b8ca0c74c87170a5cbc3f97aa0a"}, + {file = "bleak_winrt-1.2.0-cp39-cp39-win_amd64.whl", hash = "sha256:63130c11acfe75c504a79c01f9919e87f009f5e742bfc7b7a5c2a9c72bf591a7"}, +] + [[package]] name = "certifi" version = "2023.11.17" @@ -464,6 +513,50 @@ ssh = ["bcrypt (>=3.1.5)"] test = ["pretend", "pytest (>=6.2.0)", "pytest-benchmark", "pytest-cov", "pytest-xdist"] test-randomorder = ["pytest-randomly"] +[[package]] +name = "dbus-fast" +version = "2.21.0" +description = "A faster version of dbus-next" +category = "main" +optional = true +python-versions = ">=3.7,<4.0" +files = [ + {file = "dbus_fast-2.21.0-cp310-cp310-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:67fc9ca7c8fa6dec14950f3f785eab78256cb1533c0a2a6237e8dfec9dfb03a5"}, + {file = "dbus_fast-2.21.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45e9296254a8e6ba827c6ba77d9967111c9bd010525c341aede9eb4a3eb9baef"}, + {file = "dbus_fast-2.21.0-cp310-cp310-manylinux_2_31_x86_64.whl", hash = "sha256:6a46b180102be81c2146d7b924444cb414cb742e0944133ed0c1f90ebe64c1cb"}, + {file = "dbus_fast-2.21.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:4b9f40e28a12b0322eb7d32c1405e5da29a109fd51628f56e8897cf85127b6d5"}, + {file = "dbus_fast-2.21.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:fb0bb066f4c449830480cc0a20b6ccb4f2581d06876b2150b328cee184ff33ee"}, + {file = "dbus_fast-2.21.0-cp311-cp311-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:cb47669f8bd14d2d55d853b3ce501f36981a4fb088a8168adc91b19b9110a05a"}, + {file = "dbus_fast-2.21.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ef1474209830eabe482f52be85af4d38af83bb021a6fe24a28450c091af5a93"}, + {file = "dbus_fast-2.21.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:c4e623fdd921430c02c584fba922d2d67c76d5afd97013072ea9623d4fd44613"}, + {file = "dbus_fast-2.21.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b4bbd7d1deec9ecb5ef8bc5e5e9fcb87f0f8d6f3157e7e3318c54f8b013e18be"}, + {file = "dbus_fast-2.21.0-cp312-cp312-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:88f39f55e2cda6564c4fb9759aeb8354434e0d5778679f2654dfe489e3b597a4"}, + {file = "dbus_fast-2.21.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e429c8c416afad4321b9717b71e120bc19258d62d13f9b0a392f24136fb1903"}, + {file = "dbus_fast-2.21.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:63fff86527b4f9881d4a501ab613e497c72a4a97929ac2dd777cd26ebc1379fa"}, + {file = "dbus_fast-2.21.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:6c4a2b1340b59d67813b06a6b91e9092c32b81fbefbc0d38c3bcdeabac4bc902"}, + {file = "dbus_fast-2.21.0-cp37-cp37m-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:5a264d8ab81cbedb0235dc4b8dd1a5b7df44b07c42a1b0a4198fd6fec1d5dda2"}, + {file = "dbus_fast-2.21.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5a6e59927aaef2f55628ba177561c164859a36388dfc7a4c9c9d76c378efcb6"}, + {file = "dbus_fast-2.21.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:bf253e1a421ef395a43903e340d83ee9657e33ad694a5b45c9b438aa84ccc38a"}, + {file = "dbus_fast-2.21.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:23db07bae0cdd4269f40432e53b7bd82a2cc4d287be88d265f50e53f944ed3cc"}, + {file = "dbus_fast-2.21.0-cp38-cp38-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:18a10b49d208a3ea68f3a62d4d82e42638cc19521a04b5ffd3a6554f41c3e043"}, + {file = "dbus_fast-2.21.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:240d604578321f7b949b436f0f7f6097617c3a9ff70a2ce28378f5c2d9501571"}, + {file = "dbus_fast-2.21.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:921188573afadbefb37503768e2b7b3e381c648358e9d9413a7466fb6fb1d4a8"}, + {file = "dbus_fast-2.21.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:01ea44dad016e684e27615cd8ea9afd5ddfb0409d031f8ab85d540a5f78722e9"}, + {file = "dbus_fast-2.21.0-cp39-cp39-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:fabbb50bcda006b1e48d4a343ee96050c43fcbc30dd9856080378ef2b3465870"}, + {file = "dbus_fast-2.21.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:edddabd57733b4a6c5348ac2e58b662d042df9070ebac867753a93d6b5589e2a"}, + {file = "dbus_fast-2.21.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:fed1a8cd5d11416a8bcce9e588a4372d6d62c76dec275798d07095d699671885"}, + {file = "dbus_fast-2.21.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:08d490288daa283de619eaac34c6863d9bd7ecea5a5a59b02d7a59d7214a3b59"}, + {file = "dbus_fast-2.21.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:ad4aa825c58458fe333fc5e41cfd29c22947c1c30d500bccc2e39fc15787dea0"}, + {file = "dbus_fast-2.21.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d9e91c0db2e2452f9cf6c80b94edfddefd01b9b89fe8621f58296b8c29986997"}, + {file = "dbus_fast-2.21.0-pp37-pypy37_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:63affae84c36884266cb63fde0e148d4aeab26d76243e4496f3f3568f17e23d7"}, + {file = "dbus_fast-2.21.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d3bb6d7c91e35fbb5e9fb24acdceb512de31ab5a752e0818a683394e852b9654"}, + {file = "dbus_fast-2.21.0-pp38-pypy38_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:b0bd86710736d2ca438987a64e9627710886c75ff7aacb590181fa896a9102e4"}, + {file = "dbus_fast-2.21.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:19dbd4b4834947f2b78d5d29ca934e0cc2a98a4c256aa071103168f0805676ea"}, + {file = "dbus_fast-2.21.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux_2_5_i686.manylinux1_i686.manylinux2014_i686.whl", hash = "sha256:e46e1b5e98318c4f95e1f3ea041eec646979c4da069167c2b6f4cd3e54d027cc"}, + {file = "dbus_fast-2.21.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux_2_5_x86_64.manylinux1_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d87ce29c43b92123969ef9c42d7b599d315d9973ed8d69acfafc655d41c40faf"}, + {file = "dbus_fast-2.21.0.tar.gz", hash = "sha256:f582f6f16791ced6067dab325fae444edf7ce0704315b90c2a473090636a6fe0"}, +] + [[package]] name = "distlib" version = "0.3.8" @@ -631,6 +724,26 @@ files = [ {file = "imagesize-1.4.1.tar.gz", hash = "sha256:69150444affb9cb0d5cc5a92b3676f0b2fb7cd9ae39e947a5e11a36b4497cd4a"}, ] +[[package]] +name = "importlib-metadata" +version = "7.0.1" +description = "Read metadata from Python packages" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "importlib_metadata-7.0.1-py3-none-any.whl", hash = "sha256:4805911c3a4ec7c3966410053e9ec6a1fecd629117df5adee56dfc9432a1081e"}, + {file = "importlib_metadata-7.0.1.tar.gz", hash = "sha256:f238736bb06590ae52ac1fab06a3a9ef1d8dce2b7a35b5ab329371d6c8f5d2cc"}, +] + +[package.dependencies] +zipp = ">=0.5" + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +perf = ["ipython"] +testing = ["flufl.flake8", "importlib-resources (>=1.3)", "packaging", "pyfakefs", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-mypy (>=0.9.1)", "pytest-perf (>=0.9.2)", "pytest-ruff"] + [[package]] name = "jinja2" version = "3.1.2" @@ -893,6 +1006,84 @@ files = [ plugins = ["importlib-metadata"] windows-terminal = ["colorama (>=0.4.6)"] +[[package]] +name = "pyobjc-core" +version = "9.2" +description = "Python<->ObjC Interoperability Module" +category = "main" +optional = true +python-versions = ">=3.7" +files = [ + {file = "pyobjc-core-9.2.tar.gz", hash = "sha256:d734b9291fec91ff4e3ae38b9c6839debf02b79c07314476e87da8e90b2c68c3"}, + {file = "pyobjc_core-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:fa674a39949f5cde8e5c7bbcd24496446bfc67592b028aedbec7f81dc5fc4daa"}, + {file = "pyobjc_core-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:bbc8de304ee322a1ee530b4d2daca135a49b4a49aa3cedc6b2c26c43885f4842"}, + {file = "pyobjc_core-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:0fa950f092673883b8bd28bc18397415cabb457bf410920762109b411789ade9"}, + {file = "pyobjc_core-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:586e4cae966282eaa61b21cae66ccdcee9d69c036979def26eebdc08ddebe20f"}, + {file = "pyobjc_core-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:41189c2c680931c0395a55691763c481fc681f454f21bb4f1644f98c24a45954"}, + {file = "pyobjc_core-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:2d23ee539f2ba5e9f5653d75a13f575c7e36586fc0086792739e69e4c2617eda"}, + {file = "pyobjc_core-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b9809cf96678797acb72a758f34932fe8e2602d5ab7abec15c5ac68ddb481720"}, +] + +[[package]] +name = "pyobjc-framework-cocoa" +version = "9.2" +description = "Wrappers for the Cocoa frameworks on macOS" +category = "main" +optional = true +python-versions = ">=3.7" +files = [ + {file = "pyobjc-framework-Cocoa-9.2.tar.gz", hash = "sha256:efd78080872d8c8de6c2b97e0e4eac99d6203a5d1637aa135d071d464eb2db53"}, + {file = "pyobjc_framework_Cocoa-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:9e02d8a7cc4eb7685377c50ba4f17345701acf4c05b1e7480d421bff9e2f62a4"}, + {file = "pyobjc_framework_Cocoa-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:3b1e6287b3149e4c6679cdbccd8e9ef6557a4e492a892e80a77df143f40026d2"}, + {file = "pyobjc_framework_Cocoa-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:312977ce2e3989073c6b324c69ba24283de206fe7acd6dbbbaf3e29238a22537"}, + {file = "pyobjc_framework_Cocoa-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:aae7841cf40c26dd915f4dd828f91c6616e6b7998630b72e704750c09e00f334"}, + {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:739a421e14382a46cbeb9a883f192dceff368ad28ec34d895c48c0ad34cf2c1d"}, + {file = "pyobjc_framework_Cocoa-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:32d9ac1033fac1b821ddee8c68f972a7074ad8c50bec0bea9a719034c1c2fb94"}, + {file = "pyobjc_framework_Cocoa-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b236bb965e41aeb2e215d4e98a5a230d4b63252c6d26e00924ea2e69540a59d6"}, +] + +[package.dependencies] +pyobjc-core = ">=9.2" + +[[package]] +name = "pyobjc-framework-corebluetooth" +version = "9.2" +description = "Wrappers for the framework CoreBluetooth on macOS" +category = "main" +optional = true +python-versions = ">=3.7" +files = [ + {file = "pyobjc-framework-CoreBluetooth-9.2.tar.gz", hash = "sha256:cb2481b1dfe211ae9ce55f36537dc8155dbf0dc8ff26e0bc2e13f7afb0a291d1"}, + {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_universal2.whl", hash = "sha256:53d888742119d0f0c725d0b0c2389f68e8f21f0cba6d6aec288c53260a0196b6"}, + {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:179532882126526e38fe716a50fb0ee8f440e0b838d290252c515e622b5d0e49"}, + {file = "pyobjc_framework_CoreBluetooth-9.2-cp36-abi3-macosx_11_0_universal2.whl", hash = "sha256:256a5031ea9d8a7406541fa1b0dfac549b1de93deae8284605f9355b13fb58be"}, +] + +[package.dependencies] +pyobjc-core = ">=9.2" +pyobjc-framework-Cocoa = ">=9.2" + +[[package]] +name = "pyobjc-framework-libdispatch" +version = "9.2" +description = "Wrappers for libdispatch on macOS" +category = "main" +optional = true +python-versions = ">=3.7" +files = [ + {file = "pyobjc-framework-libdispatch-9.2.tar.gz", hash = "sha256:542e7f7c2b041939db5ed6f3119c1d67d73ec14a996278b92485f8513039c168"}, + {file = "pyobjc_framework_libdispatch-9.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:88d4091d4bcb5702783d6e86b4107db973425a17d1de491543f56bd348909b60"}, + {file = "pyobjc_framework_libdispatch-9.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:1a67b007113328538b57893cc7829a722270764cdbeae6d5e1460a1d911314df"}, + {file = "pyobjc_framework_libdispatch-9.2-cp312-cp312-macosx_10_9_universal2.whl", hash = "sha256:6fccea1a57436cf1ac50d9ebc6e3e725bcf77f829ba6b118e62e6ed7866d359d"}, + {file = "pyobjc_framework_libdispatch-9.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:6eba747b7ad91b0463265a7aee59235bb051fb97687f35ca2233690369b5e4e4"}, + {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2e835495860d04f63c2d2f73ae3dd79da4222864c107096dc0f99e8382700026"}, + {file = "pyobjc_framework_libdispatch-9.2-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:1b107e5c3580b09553030961ea6b17abad4a5132101eab1af3ad2cb36d0f08bb"}, + {file = "pyobjc_framework_libdispatch-9.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:83cdb672acf722717b5ecf004768f215f02ac02d7f7f2a9703da6e921ab02222"}, +] + +[package.dependencies] +pyobjc-core = ">=9.2" + [[package]] name = "pyyaml" version = "6.0.1" @@ -1046,6 +1237,7 @@ babel = ">=2.9" colorama = {version = ">=0.4.5", markers = "sys_platform == \"win32\""} docutils = ">=0.18.1,<0.21" imagesize = ">=1.3" +importlib-metadata = {version = ">=4.8", markers = "python_version < \"3.10\""} Jinja2 = ">=3.0" packaging = ">=21.0" Pygments = ">=2.14" @@ -1217,7 +1409,7 @@ six = "*" name = "typing-extensions" version = "4.9.0" description = "Backported and Experimental Type Hints for Python 3.8+" -category = "dev" +category = "main" optional = false python-versions = ">=3.8" files = [ @@ -1263,6 +1455,232 @@ platformdirs = ">=3.9.1,<5" docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx (>=7.1.2)", "sphinx-argparse (>=0.4)", "sphinxcontrib-towncrier (>=0.2.1a0)", "towncrier (>=23.6)"] test = ["covdefaults (>=2.3)", "coverage (>=7.2.7)", "coverage-enable-subprocess (>=1)", "flaky (>=3.7)", "packaging (>=23.1)", "pytest (>=7.4)", "pytest-env (>=0.8.2)", "pytest-freezer (>=0.4.8)", "pytest-mock (>=3.11.1)", "pytest-randomly (>=3.12)", "pytest-timeout (>=2.1)", "setuptools (>=68)", "time-machine (>=2.10)"] +[[package]] +name = "winrt-runtime" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-runtime-2.0.0b1.tar.gz", hash = "sha256:28db2ebe7bfb347d110224e9f23fe8079cea45af0fcbd643d039524ced07d22c"}, + {file = "winrt_runtime-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:8f812b01e2c8dd3ca68aa51a7aa02e815cc2ac3c8520a883b4ec7a4fc63afb04"}, + {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:f36f6102f9b7a08d917a6809117c085639b66be2c579f4089d3fd47b83e8f87b"}, + {file = "winrt_runtime-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:4a99f267da96edc977623355b816b46c1344c66dc34732857084417d8cf9a96b"}, + {file = "winrt_runtime-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ba998e3fc452338c5e2d7bf5174a6206580245066d60079ee4130082d0eb61c2"}, + {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e7838f0fdf5653ce245888590214177a1f54884cece2c8dfbfe3d01b2780171e"}, + {file = "winrt_runtime-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:2afa45b7385e99a63d55ccda29096e6a84fcd4c654479005c147b0e65e274abf"}, + {file = "winrt_runtime-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:edda124ff965cec3a6bfdb26fbe88e004f96975dd84115176e30c1efbcb16f4c"}, + {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:d8935951efeec6b3d546dce8f48bb203aface57a1ba991c066f0e12e84c8f91e"}, + {file = "winrt_runtime-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:509fb9a03af5e1125433f58522725716ceef040050d33625460b5a5eb98a46ac"}, + {file = "winrt_runtime-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:41138fe4642345d7143e817ce0905d82e60b3832558143e0a17bfea8654c6512"}, + {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:081a429fe85c33cb6610c4a799184b7650b30f15ab1d89866f2bda246d3a5c0a"}, + {file = "winrt_runtime-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:e6984604c6ae1f3258973ba2503d1ea5aa15e536ca41d6a131ad305ebbb6519d"}, +] + +[[package]] +name = "winrt-windows-devices-bluetooth" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Devices.Bluetooth-2.0.0b1.tar.gz", hash = "sha256:786bd43786b873a083b89debece538974f720584662a2573d6a8a8501a532860"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:79631bf3f96954da260859df9228a028835ffade0d885ba3942c5a86a853d150"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:cd85337a95065d0d2045c06db1a5edd4a447aad47cf7027818f6fb69f831c56c"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:6a963869ed003d260e90e9bedc334129303f263f068ea1c0d994df53317db2bc"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:7c5951943a3911d94a8da190f4355dc70128d7d7f696209316372c834b34d462"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:b0bb154ae92235649ed234982f609c490a467d5049c27d63397be9abbb00730e"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6688dfb0fc3b7dc517bf8cf40ae00544a50b4dec91470d37be38fc33c4523632"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:613c6ff4125df46189b3bef6d3110d94ec725d357ab734f00eedb11c4116c367"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:59c403b64e9f4e417599c6f6aea6ee6fac960597c21eac6b3fd8a84f64aa387c"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b7f6e1b9bb6e33be80045adebd252cf25cd648759fad6e86c61a393ddd709f7f"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:eae7a89106eab047e96843e28c3c6ce0886dd7dee60180a1010498925e9503f9"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:8dfd1915c894ac19dd0b24aba38ef676c92c3473c0d9826762ba9616ad7df68b"}, + {file = "winrt_Windows.Devices.Bluetooth-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:49058587e6d82ba33da0767b97a378ddfea8e3a5991bdeff680faa287bfae57e"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Devices.Bluetooth.GenericAttributeProfile[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Bluetooth.Rfcomm[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Radios[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Networking[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-devices-bluetooth-advertisement" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Devices.Bluetooth.Advertisement-2.0.0b1.tar.gz", hash = "sha256:d9050faa4377d410d4f0e9cabb5ec555a267531c9747370555ac9ec93ec9f399"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:ac9b703d16adc87c3541585525b8fcf6d84391e2fa010c2f001e714c405cc3b7"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:593cade7853a8b0770e8ef30462b5d5f477b82e17e0aa590094b1c26efd3e05a"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:574698c08895e2cfee7379bdf34a5f319fe440d7dfcc7bc9858f457c08e9712c"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:652a096f8210036bbb539d7f971eaf1f472a3aeb60b7e31278e3d0d30a355292"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:e5cfb866c44dad644fb44b441f4fdbddafc9564075f1f68f756e20f438105c67"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:6c2503eaaf5cd988b5510b86347dba45ad6ee52656f9656a1a97abae6d35386e"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:780c766725a55f4211f921c773c92c2331803e70f65d6ad6676a60f903d39a54"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:39c8633d01039eb2c2f6f20cfc43c045a333b9f3a45229e2ce443f71bb2a562c"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:eaa0d44b4158b16937eac8102249e792f0299dbb0aefc56cc9adc9552e8f9afe"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d171487e23f7671ad2923544bfa6545d0a29a1a9ae1f5c1d5e5e5f473a5d62b2"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:442eecac87653a03617e65bdb2ef79ddc0582dfdacc2be8af841fba541577f8b"}, + {file = "winrt_Windows.Devices.Bluetooth.Advertisement-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:b30ab9b8c1ecf818be08bac86bee425ef40f75060c4011d4e6c2e624a7b9916e"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-devices-bluetooth-genericattributeprofile" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1.tar.gz", hash = "sha256:93b745d51ecfb3e9d3a21623165cc065735c9e0146cb7a26744182c164e63e14"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:db740aaedd80cca5b1a390663b26c7733eb08f4c57ade6a04b055d548e9d042b"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:7c81aa6c066cdab58bcc539731f208960e094a6d48b59118898e1e804dbbdf7f"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:92277a6bbcbe2225ad1be92968af597dc77bc37a63cd729690d2d9fb5094ae25"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:6b48209669c1e214165530793cf9916ae44a0ae2618a9be7a489e8c94f7e745f"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f17216e6ce748eaef02fb0658213515d3ff31e2dbb18f070a614876f818c90d"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:db798a0f0762e390da5a9f02f822daff00692bd951a492224bf46782713b2938"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:b8d9dba04b9cfa53971c35117fc3c68c94bfa5e2ed18ce680f731743598bf246"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e5260b3f33dee8a896604297e05efc04d04298329c205a74ded8e2d6333e84b7"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:822ef539389ecb546004345c4dce8b9b7788e2e99a1d6f0947a4b123dceb7fed"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:11e6863e7a94d2b6dd76ddcd19c01e311895810a4ce6ad08c7b5534294753243"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:20de8d04c301c406362c93e78d41912aea0af23c4b430704aba329420d7c2cdf"}, + {file = "winrt_Windows.Devices.Bluetooth.GenericAttributeProfile-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:918059796f2f123216163b928ecde8ecec17994fb7a94042af07fda82c132a6d"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Devices.Bluetooth[all] (==2.0.0-beta.1)", "winrt-Windows.Devices.Enumeration[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-devices-enumeration" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Devices.Enumeration-2.0.0b1.tar.gz", hash = "sha256:8f214040e4edbe57c4943488887db89f4a00d028c34169aafd2205e228026100"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:dcb9e7d230aefec8531a46d393ecb1063b9d4b97c9f3ff2fc537ce22bdfa2444"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22a3e1fef40786cc8d51320b6f11ff25de6c674475f3ba608a46915e1dadf0f5"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:2edcfeb70a71d40622873cad96982a28e92a7ee71f33968212dd3598b2d8d469"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:ce4eb88add7f5946d2666761a97a3bb04cac2a061d264f03229c1e15dbd7ce91"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:a9001f17991572abdddab7ab074e08046e74e05eeeaf3b2b01b8b47d2879b64c"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0440b91ce144111e207f084cec6b1277162ef2df452d321951e989ce87dc9ced"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:e4fae13126f13a8d9420b74fb5a5ff6a6b2f91f7718c4be2d4a8dc1337c58f59"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:e352eebc23dc94fb79e67a056c057fb0e16c20c8cb881dc826094c20ed4791e3"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:b43f5c1f053a170e6e4b44ba69838ac223f9051adca1a56506d4c46e98d1485f"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:ed245fad8de6a134d5c3a630204e7f8238aa944a40388005bce0ce3718c410fa"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:22a9eefdbfe520778512266d0b48ff239eaa8d272fce6f5cb1ff352bed0619f4"}, + {file = "winrt_Windows.Devices.Enumeration-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:397d43f8fd2621a7719b9eab6a4a8e72a1d6fa2d9c36525a30812f8e7bad3bdf"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.ApplicationModel.Background[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Security.Credentials[all] (==2.0.0-beta.1)", "winrt-Windows.Storage.Streams[all] (==2.0.0-beta.1)", "winrt-Windows.UI.Popups[all] (==2.0.0-beta.1)", "winrt-Windows.UI[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-foundation" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Foundation-2.0.0b1.tar.gz", hash = "sha256:976b6da942747a7ca5a179a35729d8dc163f833e03b085cf940332a5e9070d54"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:5337ac1ec260132fbff868603e73a3738d4001911226e72669b3d69c8a256d5e"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:af969e5bb9e2e41e4e86a361802528eafb5eb8fe87ec1dba6048c0702d63caa8"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:bbbfa6b3c444a1074a630fd4a1b71171be7a5c9bb07c827ad9259fadaed56cf2"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:b91bd92b1854c073acd81aa87cf8df571d2151b1dd050b6181aa36f7acc43df4"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:2f5359f25703347e827dbac982150354069030f1deecd616f7ce37ad90cbcb00"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:0f1f1978173ddf0ee6262c2edb458f62d628b9fa0df10cd1e8c78c833af3197e"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:c1d23b737f733104b91c89c507b58d0b3ef5f3234a1b608ef6dfb6dbbb8777ea"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:95de6c29e9083fe63f127b965b54dfa52a6424a93a94ce87cfad4c1900a6e887"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:4707063a5a6980e3f71aebeea5ac93101c753ec13a0b47be9ea4dbc0d5ff361e"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:d0259f1f4a1b8e20d0cbd935a889c0f7234f720645590260f9cf3850fdc1e1fa"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:15c7b324d0f59839fb4492d84bb1c870881c5c67cb94ac24c664a7c4dce1c475"}, + {file = "winrt_Windows.Foundation-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:16ad741f4d38e99f8409ba5760299d0052003255f970f49f4b8ba2e0b609c8b7"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-foundation-collections" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Foundation.Collections-2.0.0b1.tar.gz", hash = "sha256:185d30f8103934124544a40aac005fa5918a9a7cb3179f45e9863bb86e22ad43"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:042142e916a170778b7154498aae61254a1a94c552954266b73479479d24f01d"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:9f68e66055121fc1e04c4fda627834aceee6fbe922e77d6ccaecf9582e714c57"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:a4609411263cc7f5e93a9a5677b21e2ef130e26f9030bfa960b3e82595324298"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:5296858aa44c53936460a119794b80eedd6bd094016c1bf96822f92cb95ea419"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3db1e1c80c97474e7c88b6052bd8982ca61723fd58ace11dc91a5522662e0b2a"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:c3a594e660c59f9fab04ae2f40bda7c809e8ec4748bada4424dfb02b43d4bfe1"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:0f355ee943ec5b835e694d97e9e93545a42d6fb984a61f442467789550d62c3f"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:c4a0cd2eb9f47c7ca3b66d12341cc822250bf26854a93fd58ab77f7a48dfab3a"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:744dbef50e8b8f34904083cae9ad43ac6e28facb9e166c4f123ce8e758141067"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:b7c767184aec3a3d7cba2cd84fadcd68106854efabef1a61092052294d6d6f4f"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:7c1ffe99c12f14fc4ab7027757780e6d850fa2fb23ec404a54311fbd9f1970d3"}, + {file = "winrt_Windows.Foundation.Collections-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:870fa040ed36066e4c240c35973d8b2e0d7c38cc6050a42d993715ec9e3b748c"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Foundation[all] (==2.0.0-beta.1)"] + +[[package]] +name = "winrt-windows-storage-streams" +version = "2.0.0b1" +description = "Python projection of Windows Runtime (WinRT) APIs" +category = "main" +optional = true +python-versions = "<3.13,>=3.9" +files = [ + {file = "winrt-Windows.Storage.Streams-2.0.0b1.tar.gz", hash = "sha256:029d67cdc9b092d56c682740fe3c42f267dc5d3346b5c0b12ebc03f38e7d2f1f"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win32.whl", hash = "sha256:49c90d4bfd539f6676226dfcb4b3574ddd6be528ffc44aa214c55af88c2de89e"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_amd64.whl", hash = "sha256:22cc82779cada84aa2633841e25b33f3357737d912a1d9ecc1ee5a8b799b5171"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp310-cp310-win_arm64.whl", hash = "sha256:b1750a111be32466f4f0781cbb5df195ac940690571dff4564492b921b162563"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win32.whl", hash = "sha256:e79b1183ab26d9b95cf3e6dbe3f488a40605174a5a112694dbb7dbfb50899daf"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_amd64.whl", hash = "sha256:3e90a1207eb3076f051a7785132f7b056b37343a68e9481a50c6defb3f660099"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp311-cp311-win_arm64.whl", hash = "sha256:4da06522b4fa9cfcc046b604cc4aa1c6a887cc4bb5b8a637ed9bff8028a860bb"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win32.whl", hash = "sha256:6f74f8ab8ac0d8de61c709043315361d8ac63f8144f3098d428472baadf8246a"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_amd64.whl", hash = "sha256:5cf7c8d67836c60392d167bfe4f98ac7abcb691bfba2d19e322d0f9181f58347"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp312-cp312-win_arm64.whl", hash = "sha256:f7f679f2c0f71791eca835856f57942ee5245094c1840a6c34bc7c2176b1bcd6"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win32.whl", hash = "sha256:5beb53429fa9a11ede56b4a7cefe28c774b352dd355f7951f2a4dd7e9ec9b39a"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_amd64.whl", hash = "sha256:f84233c4b500279d8f5840cb8c47776bc040fcecba05c6c9ab9767053698fc8b"}, + {file = "winrt_Windows.Storage.Streams-2.0.0b1-cp39-cp39-win_arm64.whl", hash = "sha256:cfb163ddbb435906f75ef92a768573b0190e194e1438cea5a4c1d4d32a6b9386"}, +] + +[package.dependencies] +winrt-runtime = "2.0.0-beta.1" + +[package.extras] +all = ["winrt-Windows.Foundation.Collections[all] (==2.0.0-beta.1)", "winrt-Windows.Foundation[all] (==2.0.0-beta.1)", "winrt-Windows.Storage[all] (==2.0.0-beta.1)", "winrt-Windows.System[all] (==2.0.0-beta.1)"] + [[package]] name = "yarl" version = "1.9.4" @@ -1367,7 +1785,26 @@ files = [ idna = ">=2.0" multidict = ">=4.0" +[[package]] +name = "zipp" +version = "3.17.0" +description = "Backport of pathlib-compatible object wrapper for zip files" +category = "dev" +optional = false +python-versions = ">=3.8" +files = [ + {file = "zipp-3.17.0-py3-none-any.whl", hash = "sha256:0e923e726174922dce09c53c59ad483ff7bbb8e572e00c7f7c46b88556409f31"}, + {file = "zipp-3.17.0.tar.gz", hash = "sha256:84e64a1c28cf7e91ed2078bb8cc8c259cb19b76942096c8d7b84947690cabaf0"}, +] + +[package.extras] +docs = ["furo", "jaraco.packaging (>=9.3)", "jaraco.tidelift (>=1.4)", "rst.linker (>=1.9)", "sphinx (<7.2.5)", "sphinx (>=3.5)", "sphinx-lint"] +testing = ["big-O", "jaraco.functools", "jaraco.itertools", "more-itertools", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=2.2)", "pytest-ignore-flaky", "pytest-mypy (>=0.9.1)", "pytest-ruff"] + +[extras] +scan = ["bleak"] + [metadata] lock-version = "2.0" -python-versions = "^3.10" -content-hash = "c76179ca5b2327f56b8157200b698fd9211de24632b3bf1cc9cffb42996a7639" +python-versions = ">=3.9,<3.13" +content-hash = "ea79322ab775017ffd7fc791885ceef05d700f808187d16971ca27d2733b8776" diff --git a/pyproject.toml b/pyproject.toml index aac8848..419c404 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,12 +7,18 @@ readme = "README.md" packages = [{ include = "findmy" }] [tool.poetry.dependencies] -python = "^3.10" +python = ">=3.9,<3.13" srp = "^1.0.20" cryptography = "^41.0.7" beautifulsoup4 = "^4.12.2" aiohttp = "^3.9.1" +# Optional dependencies +bleak = { version = "^0.21.1", optional = true } + +[tool.poetry.extras] +scan = ["bleak"] + [tool.poetry.group.dev.dependencies] pre-commit = "^3.6.0" sphinx = "^7.2.6" @@ -32,8 +38,8 @@ ignore = [ "ANN102", # annotations on `cls` "FIX002", # resolving TODOs - "D203", # one blank line before class docstring - "D212", # multi-line docstring start at first line + "D203", # one blank line before class docstring + "D212", # multi-line docstring start at first line ] line-length = 100