diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 7b3c7a3..b71cad2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,22 +17,6 @@ repos: - id: mixed-line-ending args: ['--fix=auto'] # replace 'auto' with 'lf' to enforce Linux/Mac line endings or 'crlf' for Windows -## If you want to avoid flake8 errors due to unused vars or imports: -# - repo: https://github.com/myint/autoflake -# rev: v1.4 -# hooks: -# - id: autoflake -# args: [ -# --in-place, -# --remove-all-unused-imports, -# --remove-unused-variables, -# ] - -- repo: https://github.com/PyCQA/isort - rev: 5.12.0 - hooks: - - id: isort - - repo: https://github.com/psf/black rev: 23.1.0 hooks: @@ -46,44 +30,10 @@ repos: # - id: blacken-docs # additional_dependencies: [black] -- repo: https://github.com/PyCQA/flake8 - rev: 6.0.0 +- repo: https://github.com/charliermarsh/ruff-pre-commit + rev: v0.0.261 hooks: - - id: flake8 - additional_dependencies: - - flake8-bugbear - - pep8-naming - - flake8-length - - tryceratops - - flake8-return - - flake8-newspaper-style - - flake8-warnings - - flake8-encodings - - flake8-simplify - - flake8-pie - - flake8-comprehensions - - flake8-picky-parentheses - # Documentation - - flake8-docstrings - - flake8-rst - - flake8-rst-docstrings - # Typing - - flake8-future-annotations - - flake8-annotations - - flake-type-annotations-plugin - # Tests - - flake8-pytest-style - -- repo: https://github.com/pycqa/pylint - rev: v2.16.0b1 - hooks: - - id: pylint - args: - [ - --disable=duplicate-code, - --disable=logging-fstring-interpolation, - src, - ] + - id: ruff additional_dependencies: - bleak - pytest @@ -95,12 +45,6 @@ repos: - id: interrogate args: [--verbose, --omit-covered-files, --fail-under=100] -- repo: https://github.com/asottile/pyupgrade - rev: "v3.3.1" - hooks: - - id: pyupgrade - args: ["--py37-plus"] - - repo: https://github.com/codespell-project/codespell rev: v2.2.2 hooks: diff --git a/pyproject.toml b/pyproject.toml index 89a5bed..5531c1c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,3 +7,29 @@ build-backend = "setuptools.build_meta" # For smarter version schemes and other configuration options, # check out https://github.com/pypa/setuptools_scm version_scheme = "no-guess-dev" + +[tool.ruff] +select = ["ALL"] +ignore = [ + "ANN101", # flake8-annotations + "ANN102", + "ARG002", # flake8-unused-arguments + "ARG003", + "DTZ001", # flake8-datetimez + "DTZ006", + "FBT", # flake8-boolean-trap + "T20", # flake8-print +] +# Always generate Python 3.7-compatible code +target-version = "py37" + +src = ["src", "test"] + +[tool.ruff.per-file-ignores] +"tests/*" = [ + "INP001", # implicit namespace + "S101", # assert +] + +[tool.ruff.pydocstyle] +convention = "google" diff --git a/setup.cfg b/setup.cfg index 35954bb..b2f176f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -114,46 +114,6 @@ testpaths = tests no_vcs = 1 formats = bdist_wheel -[flake8] -# Some sane defaults for the code style checker flake8 -max_line_length = 88 -# flake8-docstrings config -docstring-convention = google -# For flake8-rst-docstrings: -rst-roles = - class, - func, - ref, - meth, - mod, - obj, -rst-directives = - envvar, - exception, -rst-substitutions = - version, -extend_ignore = E203, W503 -# ^ Black-compatible -# E203 and W503 have edge cases handled by black - RST307, - # Google Python style is not RST until after processed by Napoleon - # See https://github.com/peterjc/flake8-rst-docstrings/issues/17 - RST201,RST203,RST301, - # Missing type annotations for self and cls in (class) method - ANN101,ANN102, - # Handled by flake8-length - E501, W505, - # Opinionated check by flake8-picky-parentheses - PAR101, -exclude = - .tox - build - dist - .eggs - docs/conf.py -# flake8-pytest-style config -pytest-parametrize-names-type = csv - [pyscaffold] # PyScaffold's parameters when the project was created. # This will be used when updating. Do not change! diff --git a/setup.py b/setup.py index 88744ca..97c9ce3 100644 --- a/setup.py +++ b/setup.py @@ -9,11 +9,11 @@ if __name__ == "__main__": try: setup(use_scm_version={"version_scheme": "no-guess-dev"}) - except: # noqa + except: print( "\n\nAn error occurred while building the project, " "please ensure you have the most updated version of setuptools, " "setuptools_scm and wheel with:\n" - " pip install -U setuptools setuptools_scm wheel\n\n" + " pip install -U setuptools setuptools_scm wheel\n\n", ) raise diff --git a/src/bluetooth_clocks/__init__.py b/src/bluetooth_clocks/__init__.py index 6fc0e1a..081331c 100644 --- a/src/bluetooth_clocks/__init__.py +++ b/src/bluetooth_clocks/__init__.py @@ -13,12 +13,16 @@ from pathlib import Path from pkgutil import iter_modules from time import time -from typing import ClassVar, Type # noqa: F401 -from uuid import UUID +from typing import TYPE_CHECKING, ClassVar + +if TYPE_CHECKING: + from uuid import UUID from bleak import BleakClient -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementData + +if TYPE_CHECKING: + from bleak.backends.device import BLEDevice + from bleak.backends.scanner import AdvertisementData from bluetooth_clocks.exceptions import TimeNotReadableError, UnsupportedDeviceError @@ -29,7 +33,7 @@ try: # Change here if project is renamed and does not equal the package name - dist_name = "bluetooth-clocks" # pylint: disable=invalid-name + dist_name = "bluetooth-clocks" __version__ = version(dist_name) except PackageNotFoundError: # pragma: no cover __version__ = "unknown" @@ -83,33 +87,33 @@ class BluetoothClock(ABC): have a name. """ - DEVICE_TYPE: ClassVar[str] # noqa: CCE001 + DEVICE_TYPE: ClassVar[str] """The name of the device type.""" - SERVICE_UUID: ClassVar[UUID] # noqa: CCE001 + SERVICE_UUID: ClassVar[UUID] """The UUID of the service used to read/write the time.""" - CHAR_UUID: ClassVar[UUID] # noqa: CCE001 + CHAR_UUID: ClassVar[UUID] """The UUID of the characteristic used to read/write the time.""" - TIME_GET_FORMAT: ClassVar[str | None] # noqa: CCE001 + TIME_GET_FORMAT: ClassVar[str | None] """The format string to convert bytes read from the device to a time. This is ``None`` if the device doesn't support reading the time. """ - TIME_SET_FORMAT: ClassVar[str] # noqa: CCE001 + TIME_SET_FORMAT: ClassVar[str] """The format string to convert a time to bytes written to the device.""" - WRITE_WITH_RESPONSE: ClassVar[bool] # noqa: CCE001 + WRITE_WITH_RESPONSE: ClassVar[bool] """``True`` if the bytes to set the time should use write with response.""" - LOCAL_NAME: ClassVar[str | None] # noqa: CCE001 + LOCAL_NAME: ClassVar[str | None] """The local name used to recognize this type of device. This is ``None`` if the local name isn't used to recognize the device.""" - LOCAL_NAME_STARTS_WITH: ClassVar[bool | None] # noqa: CCE001 + LOCAL_NAME_STARTS_WITH: ClassVar[bool | None] """Whether the local name should start with `LOCAL_NAME`. ``True`` if the start of `LOCAL_NAME` is used to recognize this type of device. @@ -128,7 +132,9 @@ def __init__(self, device: BLEDevice) -> None: @classmethod def create_from_advertisement( - cls, device: BLEDevice, advertisement_data: AdvertisementData + cls, + device: BLEDevice, + advertisement_data: AdvertisementData, ) -> BluetoothClock: """Create object of a :class:`BluetoothClock` subclass from advertisement data. @@ -175,7 +181,7 @@ def is_readable(cls) -> bool: @classmethod def recognize( cls, - device: BLEDevice, # pylint: disable=unused-argument + device: BLEDevice, advertisement_data: AdvertisementData, ) -> bool: """Recognize this device type from advertisement data. @@ -246,9 +252,7 @@ def recognize_from_local_name( return local_name.startswith(cls.LOCAL_NAME) return local_name == cls.LOCAL_NAME - def get_time_from_bytes( # pylint: disable=unused-argument - self, time_bytes: bytes - ) -> float: + def get_time_from_bytes(self, time_bytes: bytes) -> float: """Convert bytes read from a device to a timestamp. Override this method in a subclass for a device that supports getting the time. @@ -267,12 +271,12 @@ def get_time_from_bytes( # pylint: disable=unused-argument >>> from bluetooth_clocks.devices.xiaomi import LYWSD02 >>> from bleak.backends.device import BLEDevice >>> from datetime import datetime - >>> clock = LYWSD02(BLEDevice("E7:2E:00:B1:38:96")) + >>> clock = LYWSD02(BLEDevice("E7:2E:00:B1:38:96", "", {}, -67)) >>> timestamp = clock.get_time_from_bytes( ... bytes([0xdd, 0xbc, 0xb9, 0x63, 0x00])) >>> print(datetime.utcfromtimestamp(timestamp).strftime("%Y-%m-%d %H:%M:%S")) 2023-01-07 18:41:33 - """ + """ # noqa: E501 raise TimeNotReadableError @abstractmethod @@ -294,7 +298,7 @@ def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: >>> from bluetooth_clocks.devices.thermopro import TP393 >>> from bleak.backends.device import BLEDevice >>> from datetime import datetime - >>> clock = TP393(BLEDevice("10:76:36:14:2A:3D")) + >>> clock = TP393(BLEDevice("10:76:36:14:2A:3D", "TP393 (2A3D)", {}, -67)) >>> timestamp = datetime.fromisoformat("2023-01-07 17:32:50").timestamp() >>> clock.get_bytes_from_time(timestamp, ampm=True).hex() 'a517010711203206005a' @@ -321,7 +325,9 @@ async def get_time(self) -> float: return self.get_time_from_bytes(time_bytes) async def set_time( - self, timestamp: float | None = None, ampm: bool = False + self, + timestamp: float | None = None, + ampm: bool = False, ) -> None: """Set the time of the Bluetooth clock. @@ -347,7 +353,7 @@ async def set_time( # Iterate through the modules in the module `device`. package_dir = Path(__file__).resolve().parent / "devices" -for _, module_name, _ in iter_modules([str(package_dir)]): # type: ignore +for _, module_name, _ in iter_modules([str(package_dir)]): # type: ignore[assignment] # Import the module and iterate through its attributes module = import_module(f"{__name__}.devices.{module_name}") for attribute_name in dir(module): diff --git a/src/bluetooth_clocks/__main__.py b/src/bluetooth_clocks/__main__.py index 0d1beb4..56eb06f 100644 --- a/src/bluetooth_clocks/__main__.py +++ b/src/bluetooth_clocks/__main__.py @@ -79,7 +79,8 @@ def parse_args(args: list[str]) -> Namespace: # Parser for the "discover" subcommand parser_discover = subparsers.add_parser( - "discover", help="discover supported Bluetooth clocks" + "discover", + help="discover supported Bluetooth clocks", ) parser_discover.add_argument( "-s", @@ -92,7 +93,8 @@ def parse_args(args: list[str]) -> Namespace: # Parser for the "get" subcommand parser_get = subparsers.add_parser( - "get", help="get the time from a Bluetooth clock" + "get", + help="get the time from a Bluetooth clock", ) parser_get.add_argument( "-a", @@ -128,7 +130,7 @@ def parse_args(args: list[str]) -> Namespace: "-t", "--time", type=str, - help="the time to set, in ISO 8601 format (e.g. 2023-01-10T16:20, default: current time)", + help="the time to set, in ISO 8601 format (e.g. 2023-01-10T16:20, default: current time)", # noqa: E501 ) parser_set.add_argument( "-p", @@ -150,7 +152,10 @@ def setup_logging(loglevel: int) -> None: """ logformat = "[%(asctime)s] %(levelname)s:%(name)s:%(message)s" logging.basicConfig( - level=loglevel, stream=sys.stdout, format=logformat, datefmt="%Y-%m-%d %H:%M:%S" + level=loglevel, + stream=sys.stdout, + format=logformat, + datefmt="%Y-%m-%d %H:%M:%S", ) diff --git a/src/bluetooth_clocks/devices/current_time_service.py b/src/bluetooth_clocks/devices/current_time_service.py index 03e70a9..7c53c85 100644 --- a/src/bluetooth_clocks/devices/current_time_service.py +++ b/src/bluetooth_clocks/devices/current_time_service.py @@ -6,10 +6,12 @@ import struct from datetime import datetime +from typing import TYPE_CHECKING from uuid import UUID -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementData +if TYPE_CHECKING: + from bleak.backends.device import BLEDevice + from bleak.backends.scanner import AdvertisementData from bluetooth_clocks import MICROSECONDS, BluetoothClock from bluetooth_clocks.exceptions import InvalidTimeBytesError @@ -42,7 +44,9 @@ class CurrentTimeService(BluetoothClock): @classmethod def recognize( - cls, device: BLEDevice, advertisement_data: AdvertisementData + cls, + device: BLEDevice, + advertisement_data: AdvertisementData, ) -> bool: """Recognize the Current Time Service from advertisement data. @@ -95,7 +99,11 @@ def get_time_from_bytes(self, time_bytes: bytes) -> float: raise InvalidTimeBytesError(time_bytes) from exception return date_time.timestamp() - def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: + def get_bytes_from_time( + self, + timestamp: float, + ampm: bool = False, + ) -> bytes: """Generate the bytes to set the time on the Current Time Service. Args: @@ -134,7 +142,9 @@ class InfiniTime(CurrentTimeService): @classmethod def recognize( - cls, device: BLEDevice, advertisement_data: AdvertisementData + cls, + device: BLEDevice, + advertisement_data: AdvertisementData, ) -> bool: """Recognize the PineTime with InfiniTime firmware from advertisement data. diff --git a/src/bluetooth_clocks/devices/pvvx.py b/src/bluetooth_clocks/devices/pvvx.py index 92681f1..ef5c99d 100644 --- a/src/bluetooth_clocks/devices/pvvx.py +++ b/src/bluetooth_clocks/devices/pvvx.py @@ -3,10 +3,12 @@ import struct from time import localtime +from typing import TYPE_CHECKING from uuid import UUID -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementData +if TYPE_CHECKING: + from bleak.backends.device import BLEDevice + from bleak.backends.scanner import AdvertisementData from bluetooth_clocks import BluetoothClock @@ -32,8 +34,10 @@ class PVVX(BluetoothClock): @classmethod def recognize( - cls, device: BLEDevice, advertisement_data: AdvertisementData - ) -> bool: + cls, + device: BLEDevice, + advertisement_data: AdvertisementData, + ) -> bool: # ARG003 """Recognize the PVVX device from advertisement data. This checks whether the advertisement has service data with service UUID @@ -49,7 +53,11 @@ def recognize( """ return str(cls.SERVICE_DATA_UUID) in advertisement_data.service_data - def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: + def get_bytes_from_time( + self, + timestamp: float, + ampm: bool = False, + ) -> bytes: """Generate the bytes to set the time on the PVVX device. Args: diff --git a/src/bluetooth_clocks/devices/qingping.py b/src/bluetooth_clocks/devices/qingping.py index 9a3ae2a..cc85cc1 100644 --- a/src/bluetooth_clocks/devices/qingping.py +++ b/src/bluetooth_clocks/devices/qingping.py @@ -40,14 +40,18 @@ class CGC1(BluetoothClock): LOCAL_NAME_STARTS_WITH = False """The local name should exactly match `LOCAL_NAME`.""" - def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: + def get_bytes_from_time( + self, + timestamp: float, + ampm: bool = False, + ) -> bytes: """Generate the bytes to set the time on the Qingping BT Clock Lite. Args: timestamp (float): The time encoded as a Unix timestamp. ampm (bool): ``True`` if the device should show the time with AM/PM, ``False`` if it should use 24-hour format. The Qingping BT Clock - Lite ignores this argument, as it doesn’t support this option. + Lite ignores this argument, as it doesn`t support this option. Returns: bytes: The bytes needed to set the time of the device to `timestamp`. diff --git a/src/bluetooth_clocks/devices/thermopro.py b/src/bluetooth_clocks/devices/thermopro.py index d55d669..246f4a5 100644 --- a/src/bluetooth_clocks/devices/thermopro.py +++ b/src/bluetooth_clocks/devices/thermopro.py @@ -23,7 +23,7 @@ class TPXXX(BluetoothClock): """The UUID of the characteristic used to write the time.""" TIME_GET_FORMAT = None - """ThermoPro devices don’t support reading the time.""" + """ThermoPro devices don`t support reading the time.""" TIME_SET_FORMAT = "BBBBBBBBBB" """The format string to convert a time to bytes written to the device. @@ -61,7 +61,7 @@ def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: ) -class TP358(TPXXX): # pylint: disable=too-few-public-methods +class TP358(TPXXX): """Bluetooth clock support for the ThermoPro TP358.""" DEVICE_TYPE = "ThermoPro TP358" @@ -72,7 +72,7 @@ class TP358(TPXXX): # pylint: disable=too-few-public-methods """The local name should start with `LOCAL_NAME`.""" -class TP393(TPXXX): # pylint: disable=too-few-public-methods +class TP393(TPXXX): """Bluetooth clock support for the ThermoPro TP393.""" DEVICE_TYPE = "ThermoPro TP393" diff --git a/src/bluetooth_clocks/devices/xiaomi.py b/src/bluetooth_clocks/devices/xiaomi.py index 5d7b1c6..522e396 100644 --- a/src/bluetooth_clocks/devices/xiaomi.py +++ b/src/bluetooth_clocks/devices/xiaomi.py @@ -52,7 +52,11 @@ def get_time_from_bytes(self, time_bytes: bytes) -> float: raise InvalidTimeBytesError(time_bytes) from exception return float(time_time) - def get_bytes_from_time(self, timestamp: float, ampm: bool = False) -> bytes: + def get_bytes_from_time( + self, + timestamp: float, + ampm: bool = False, + ) -> bytes: """Generate the bytes to set the time on the Xiaomi LYWSD02. Args: diff --git a/src/bluetooth_clocks/exceptions.py b/src/bluetooth_clocks/exceptions.py index eae4535..8417d46 100644 --- a/src/bluetooth_clocks/exceptions.py +++ b/src/bluetooth_clocks/exceptions.py @@ -10,7 +10,7 @@ class InvalidTimeBytesError(BluetoothClocksError): class TimeNotReadableError(BluetoothClocksError): - """Exception raised when trying to read the time on a device that doesn't support this.""" + """Exception raised when reading the time on a device that doesn't support this.""" class UnsupportedDeviceError(BluetoothClocksError): diff --git a/src/bluetooth_clocks/scanners.py b/src/bluetooth_clocks/scanners.py index 1bffd81..522f19a 100644 --- a/src/bluetooth_clocks/scanners.py +++ b/src/bluetooth_clocks/scanners.py @@ -3,11 +3,13 @@ import asyncio import logging -from typing import Callable +from typing import TYPE_CHECKING, Callable from bleak import BleakScanner -from bleak.backends.device import BLEDevice -from bleak.backends.scanner import AdvertisementData + +if TYPE_CHECKING: + from bleak.backends.device import BLEDevice + from bleak.backends.scanner import AdvertisementData from bluetooth_clocks import BluetoothClock, supported_devices from bluetooth_clocks.exceptions import UnsupportedDeviceError @@ -24,7 +26,7 @@ async def find_clock(address: str, scan_duration: float = 5.0) -> BluetoothClock 5 seconds. Raises: - UnsupportedDeviceError: If the device with address `address` isn’t + UnsupportedDeviceError: If the device with address `address` isn`t supported. Returns: @@ -37,12 +39,17 @@ def device_found(device: BLEDevice, advertisement_data: AdvertisementData) -> No """Try to recognize device as a Bluetooth clock.""" nonlocal found_clock if device.address == address: - _logger.info(f"Device with address {address} found") + _logger.info( + "Device with address {address} found", + extra={"address": address}, + ) found_clock = BluetoothClock.create_from_advertisement( - device, advertisement_data + device, + advertisement_data, ) _logger.info( - f"Device with address {address} recognized as {found_clock.DEVICE_TYPE}" + "Device with address {address} recognized as {device_type}", + extra={"address": address, "device_type": found_clock.DEVICE_TYPE}, ) scanner = BleakScanner(detection_callback=device_found) @@ -52,11 +59,12 @@ def device_found(device: BLEDevice, advertisement_data: AdvertisementData) -> No await asyncio.sleep(scan_duration) await scanner.stop() - return found_clock # noqa: R504 + return found_clock async def discover_clocks( - callback: Callable[[BluetoothClock], None], scan_duration: float = 5.0 + callback: Callable[[BluetoothClock], None], + scan_duration: float = 5.0, ) -> None: """Discover Bluetooth clocks. @@ -76,11 +84,19 @@ def device_found(device: BLEDevice, advertisement_data: AdvertisementData) -> No if address not in found_addresses: try: clock = BluetoothClock.create_from_advertisement( - device, advertisement_data + device, + advertisement_data, ) name = advertisement_data.local_name clock_type = clock.DEVICE_TYPE - _logger.info(f"Found a {clock_type}: address {address}, name {name}") + _logger.info( + "Found a {clock_type}: address {address}, name {device_name}", + extra={ + "clock_type": clock_type, + "address": address, + "device_name": name, + }, + ) callback(clock) except UnsupportedDeviceError: # Just ignore devices we don't recognize as a clock. @@ -89,10 +105,16 @@ def device_found(device: BLEDevice, advertisement_data: AdvertisementData) -> No # Don't try to recognize the device with the same address again. found_addresses.append(address) - _logger.info(f"Supported devices: {', '.join(supported_devices())}") + _logger.info( + "Supported devices: {devices}", + extra={"devices": ", ".join(supported_devices())}, + ) scanner = BleakScanner(detection_callback=device_found) - _logger.info(f"Scanning for supported clocks for {scan_duration} seconds...") + _logger.info( + "Scanning for supported clocks for {scan_duration} seconds...", + extra={"scan_duration": scan_duration}, + ) await scanner.start() await asyncio.sleep(scan_duration) diff --git a/tests/conftest.py b/tests/conftest.py index b629b46..00cc859 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,5 +5,3 @@ - https://docs.pytest.org/en/stable/fixture.html - https://docs.pytest.org/en/stable/writing_plugins.html """ - -# import pytest diff --git a/tests/test_bluetooth_clock.py b/tests/test_bluetooth_clock.py index 9d99998..1012982 100644 --- a/tests/test_bluetooth_clock.py +++ b/tests/test_bluetooth_clock.py @@ -21,7 +21,7 @@ def test_create_from_advertisement() -> None: """Test whether an object of the right subclass is created from an advertisement.""" assert isinstance( BluetoothClock.create_from_advertisement( - BLEDevice("E7:2E:00:B1:38:96", "LYWSD02"), + BLEDevice("E7:2E:00:B1:38:96", "LYWSD02", {}, -67), AdvertisementData( local_name="LYWSD02", manufacturer_data={}, @@ -47,8 +47,8 @@ def test_create_from_advertisement() -> None: 0x02, 0xD5, 0x00, - ] - ) + ], + ), }, service_uuids=[ "0000181a-0000-1000-8000-00805f9b34fb", @@ -65,11 +65,11 @@ def test_create_from_advertisement_unknown() -> None: """Test whether no subclass is created from an unknown advertisement.""" with pytest.raises(UnsupportedDeviceError): BluetoothClock.create_from_advertisement( - BLEDevice("45:B4:07:8A:66:6A"), + BLEDevice("45:B4:07:8A:66:6A", "", {}, -67), AdvertisementData( local_name=None, manufacturer_data={ - 0x004C: bytes([0x10, 0x05, 0x47, 0x1C, 0x7F, 0xF1, 0x93]) + 0x004C: bytes([0x10, 0x05, 0x47, 0x1C, 0x7F, 0xF1, 0x93]), }, platform_data=(), rssi=-67, diff --git a/tests/test_current_time_service.py b/tests/test_current_time_service.py index 48afe88..6982e0c 100644 --- a/tests/test_current_time_service.py +++ b/tests/test_current_time_service.py @@ -25,16 +25,16 @@ ], ) def test_in_supported_devices(model: type[BluetoothClock]) -> None: - """Test whether the Current Time Service model is in the list of supported devices.""" + """Test whether Current Time Service model is in the list of supported devices.""" assert model.DEVICE_TYPE in supported_devices() @pytest.mark.parametrize( - "model, device, advertisement_data", + ("model", "device", "advertisement_data"), [ ( CurrentTimeService, - BLEDevice("EB:76:55:B9:56:18", "F15"), + BLEDevice("EB:76:55:B9:56:18", "F15", {}, -67), AdvertisementData( local_name="F15", manufacturer_data={}, @@ -53,7 +53,7 @@ def test_in_supported_devices(model: type[BluetoothClock]) -> None: ), ( InfiniTime, - BLEDevice("F3:BE:3E:97:17:A4", "InfiniTime"), + BLEDevice("F3:BE:3E:97:17:A4", "InfiniTime", {}, -67), AdvertisementData( local_name="InfiniTime", manufacturer_data={}, @@ -71,7 +71,7 @@ def test_recognize( device: BLEDevice, advertisement_data: AdvertisementData, ) -> None: - """Test whether the Current Time Service model is recognized from an advertisement.""" + """Test whether Current Time Service model is recognized from an advertisement.""" assert model.recognize(device=device, advertisement_data=advertisement_data) @@ -88,24 +88,27 @@ def test_readable(model: type[BluetoothClock]) -> None: @pytest.mark.parametrize( - "model, device, time_bytes, time", + ("model", "device", "time_bytes", "time"), [ ( CurrentTimeService, - BLEDevice("EB:76:55:B9:56:18"), + BLEDevice("EB:76:55:B9:56:18", "", {}, -67), bytes([0xE7, 0x07, 0x01, 0x07, 0x12, 0x29, 0x21, 0x06, 0x00]), "2023-01-07 18:41:33", ), ( InfiniTime, - BLEDevice("F3:BE:3E:97:17:A4"), + BLEDevice("F3:BE:3E:97:17:A4", "", {}, -67), bytes([0xE7, 0x07, 0x01, 0x07, 0x12, 0x29, 0x21, 0x06, 0x00]), "2023-01-07 18:41:33", ), ], ) def test_get_time_from_bytes( - model: type[BluetoothClock], device: BLEDevice, time_bytes: bytes, time: str + model: type[BluetoothClock], + device: BLEDevice, + time_bytes: bytes, + time: str, ) -> None: """Test the conversion from bytes to a timestamp.""" timestamp = datetime.fromisoformat(time).timestamp() @@ -113,22 +116,24 @@ def test_get_time_from_bytes( @pytest.mark.parametrize( - "model, device, time_bytes", + ("model", "device", "time_bytes"), [ ( CurrentTimeService, - BLEDevice("EB:76:55:B9:56:18"), + BLEDevice("EB:76:55:B9:56:18", "", {}, -67), bytes([0x2A]), ), ( InfiniTime, - BLEDevice("F3:BE:3E:97:17:A4"), + BLEDevice("F3:BE:3E:97:17:A4", "", {}, -67), bytes([0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09]), ), ], ) def test_get_time_from_bytes_invalid( - model: type[BluetoothClock], device: BLEDevice, time_bytes: bytes + model: type[BluetoothClock], + device: BLEDevice, + time_bytes: bytes, ) -> None: """Test whether trying to convert invalid bytes raises an exception.""" with pytest.raises(InvalidTimeBytesError): @@ -136,24 +141,27 @@ def test_get_time_from_bytes_invalid( @pytest.mark.parametrize( - "model, device, time, time_bytes", + ("model", "device", "time", "time_bytes"), [ ( CurrentTimeService, - BLEDevice("EB:76:55:B9:56:18"), + BLEDevice("EB:76:55:B9:56:18", "", {}, -67), "2023-01-07 18:41:33", bytes([0xE7, 0x07, 0x01, 0x07, 0x12, 0x29, 0x21, 0x06, 0x00, 0x00]), ), ( InfiniTime, - BLEDevice("F3:BE:3E:97:17:A4"), + BLEDevice("F3:BE:3E:97:17:A4", "", {}, -67), "2023-01-07 18:41:33", bytes([0xE7, 0x07, 0x01, 0x07, 0x12, 0x29, 0x21, 0x06, 0x00, 0x00]), ), ], ) def test_get_bytes_from_time( - model: type[BluetoothClock], device: BLEDevice, time: str, time_bytes: bytes + model: type[BluetoothClock], + device: BLEDevice, + time: str, + time_bytes: bytes, ) -> None: """Test the command to set the time.""" timestamp = datetime.fromisoformat(time).timestamp() diff --git a/tests/test_pvvx.py b/tests/test_pvvx.py index c6bdf68..5813b00 100644 --- a/tests/test_pvvx.py +++ b/tests/test_pvvx.py @@ -32,7 +32,7 @@ def test_in_supported_devices() -> None: def test_recognize() -> None: """Test whether PVVX is recognized from an advertisement.""" assert PVVX.recognize( - BLEDevice("A4:C1:38:D9:01:10", "LYWSD03MMC"), + BLEDevice("A4:C1:38:D9:01:10", "LYWSD03MMC", {}, -67), AdvertisementData( local_name="LYWSD03MMC", manufacturer_data={}, @@ -56,8 +56,8 @@ def test_recognize() -> None: 0x64, 0xDC, 0x05, - ] - ) + ], + ), }, service_uuids=[], tx_power=0, @@ -73,8 +73,8 @@ def test_readable() -> None: def test_get_time_from_bytes() -> None: """Test the conversion from bytes to a timestamp.""" with pytest.raises(TimeNotReadableError): - PVVX(BLEDevice("A4:C1:38:D9:01:10")).get_time_from_bytes( - bytes([0x23, 0xDD, 0xBC, 0xB9, 0x63]) + PVVX(BLEDevice("A4:C1:38:D9:01:10", "", {}, -67)).get_time_from_bytes( + bytes([0x23, 0xDD, 0xBC, 0xB9, 0x63]), ) @@ -82,6 +82,6 @@ def test_get_time_from_bytes() -> None: @time_machine.travel(datetime(2023, 1, 7, 18, 41, tzinfo=CET_TZ), tick=False) def test_get_bytes_from_time() -> None: """Test the command to set the time.""" - assert PVVX(BLEDevice("A4:C1:38:D9:01:10")).get_bytes_from_time(time()) == bytes( - [0x23, 0xBC, 0xBC, 0xB9, 0x63] - ) + assert PVVX(BLEDevice("A4:C1:38:D9:01:10", "", {}, -67)).get_bytes_from_time( + time(), + ) == bytes([0x23, 0xBC, 0xBC, 0xB9, 0x63]) diff --git a/tests/test_qingping.py b/tests/test_qingping.py index a6de188..ee1bcea 100644 --- a/tests/test_qingping.py +++ b/tests/test_qingping.py @@ -32,7 +32,7 @@ def test_in_supported_devices() -> None: def test_recognize() -> None: """Test whether the Qingping BT Clock Lite is recognized from an advertisement.""" assert CGC1.recognize( - BLEDevice("58:2D:34:54:2D:2C", "Qingping BT Clock Lite"), + BLEDevice("58:2D:34:54:2D:2C", "Qingping BT Clock Lite", {}, -67), AdvertisementData( local_name="Qingping BT Clock Lite", manufacturer_data={}, @@ -58,8 +58,8 @@ def test_recognize() -> None: 0x02, 0x01, 0x64, - ] - ) + ], + ), }, service_uuids=[], tx_power=0, @@ -75,8 +75,8 @@ def test_not_readable() -> None: def test_get_time_from_bytes() -> None: """Test that this class doesn't support conversion from bytes to a timestamp.""" with pytest.raises(TimeNotReadableError): - CGC1(BLEDevice("58:2D:34:54:2D:2C")).get_time_from_bytes( - bytes([0x05, 0x09, 0x00, 0xF6, 0xAE, 0x63]) + CGC1(BLEDevice("58:2D:34:54:2D:2C", "", {}, -67)).get_time_from_bytes( + bytes([0x05, 0x09, 0x00, 0xF6, 0xAE, 0x63]), ) @@ -84,6 +84,6 @@ def test_get_time_from_bytes() -> None: @time_machine.travel(datetime(2022, 12, 30, 16, 30, tzinfo=CET_TZ), tick=False) def test_get_bytes_from_time() -> None: """Test the command to set the time.""" - assert CGC1(BLEDevice("58:2D:34:54:2D:2C")).get_bytes_from_time(time()) == bytes( - [0x05, 0x09, 0x08, 0x12, 0xAF, 0x63] - ) + assert CGC1(BLEDevice("58:2D:34:54:2D:2C", "", {}, -67)).get_bytes_from_time( + time(), + ) == bytes([0x05, 0x09, 0x08, 0x12, 0xAF, 0x63]) diff --git a/tests/test_thermopro.py b/tests/test_thermopro.py index 468db93..d03175c 100644 --- a/tests/test_thermopro.py +++ b/tests/test_thermopro.py @@ -30,11 +30,11 @@ def test_in_supported_devices(model: type[BluetoothClock]) -> None: @pytest.mark.parametrize( - "model, device, advertisement_data", + ("model", "device", "advertisement_data"), [ ( TP358, - BLEDevice("BC:C7:DA:6A:52:C6", "TP358 (52C6)"), + BLEDevice("BC:C7:DA:6A:52:C6", "TP358 (52C6)", {}, -67), AdvertisementData( local_name="TP358 (52C6)", manufacturer_data={0xD2C2: bytes([0x00, 0x3C, 0x02, 0x2C])}, @@ -47,7 +47,7 @@ def test_in_supported_devices(model: type[BluetoothClock]) -> None: ), ( TP393, - BLEDevice("10:76:36:14:2A:3D", "TP393 (2A3D)"), + BLEDevice("10:76:36:14:2A:3D", "TP393 (2A3D)", {}, -67), AdvertisementData( local_name="TP393 (2A3D)", manufacturer_data={0xD0C2: bytes([0x00, 0x3F, 0x02, 0x2C])}, @@ -82,22 +82,24 @@ def test_not_readable(model: type[BluetoothClock]) -> None: @pytest.mark.parametrize( - "model, device, time_bytes", + ("model", "device", "time_bytes"), [ ( TP358, - BLEDevice("BC:C7:DA:6A:52:C6"), + BLEDevice("BC:C7:DA:6A:52:C6", "", {}, -67), bytes([0xA5, 0x16, 0x0C, 0x1D, 0x12, 0x09, 0x01, 0x04, 0x01, 0x5A]), ), ( TP393, - BLEDevice("10:76:36:14:2A:3D"), + BLEDevice("10:76:36:14:2A:3D", "", {}, -67), bytes([0xA5, 0x17, 0x01, 0x07, 0x11, 0x20, 0x32, 0x06, 0x00, 0x5A]), ), ], ) def test_get_time_from_bytes( - model: type[BluetoothClock], device: BLEDevice, time_bytes: bytes + model: type[BluetoothClock], + device: BLEDevice, + time_bytes: bytes, ) -> None: """Test that this class doesn't support conversion from bytes to a timestamp.""" with pytest.raises(TimeNotReadableError): @@ -105,18 +107,18 @@ def test_get_time_from_bytes( @pytest.mark.parametrize( - "model, device, time, ampm, time_bytes", + ("model", "device", "time", "ampm", "time_bytes"), [ ( TP358, - BLEDevice("BC:C7:DA:6A:52:C6"), + BLEDevice("BC:C7:DA:6A:52:C6", "", {}, -67), "2022-12-29 18:09:01", False, bytes([0xA5, 0x16, 0x0C, 0x1D, 0x12, 0x09, 0x01, 0x04, 0x01, 0x5A]), ), ( TP393, - BLEDevice("10:76:36:14:2A:3D"), + BLEDevice("10:76:36:14:2A:3D", "", {}, -67), "2023-01-07 17:32:50", True, bytes([0xA5, 0x17, 0x01, 0x07, 0x11, 0x20, 0x32, 0x06, 0x00, 0x5A]), diff --git a/tests/test_xiaomi.py b/tests/test_xiaomi.py index c23e861..ca65202 100644 --- a/tests/test_xiaomi.py +++ b/tests/test_xiaomi.py @@ -32,7 +32,7 @@ def test_in_supported_devices() -> None: def test_recognize() -> None: """Test whether the Xiaomi LYWSD02 is recognized from an advertisement.""" assert LYWSD02.recognize( - BLEDevice("E7:2E:00:B1:38:96", "LYWSD02"), + BLEDevice("E7:2E:00:B1:38:96", "LYWSD02", {}, -67), AdvertisementData( local_name="LYWSD02", manufacturer_data={}, @@ -58,8 +58,8 @@ def test_recognize() -> None: 0x02, 0xD5, 0x00, - ] - ) + ], + ), }, service_uuids=[ "0000181a-0000-1000-8000-00805f9b34fb", @@ -79,8 +79,8 @@ def test_get_time_from_bytes() -> None: """Test the conversion from bytes to a timestamp.""" timestamp = datetime.fromisoformat("2023-01-07 18:41:33+00:00").timestamp() assert ( - LYWSD02(BLEDevice("E7:2E:00:B1:38:96")).get_time_from_bytes( - bytes([0xDD, 0xBC, 0xB9, 0x63, 0x00]) + LYWSD02(BLEDevice("E7:2E:00:B1:38:96", "", {}, -67)).get_time_from_bytes( + bytes([0xDD, 0xBC, 0xB9, 0x63, 0x00]), ) == timestamp ) @@ -89,13 +89,15 @@ def test_get_time_from_bytes() -> None: def test_get_time_from_bytes_invalid() -> None: """Test whether trying to convert invalid bytes raises an exception.""" with pytest.raises(InvalidTimeBytesError): - LYWSD02(BLEDevice("E7:2E:00:B1:38:96")).get_time_from_bytes(bytes([0x2A])) + LYWSD02(BLEDevice("E7:2E:00:B1:38:96", "", {}, -67)).get_time_from_bytes( + bytes([0x2A]), + ) @pytest.mark.skipif(sys.platform.startswith("win"), reason="timezone problem") @time_machine.travel(datetime(2023, 1, 7, 18, 41, tzinfo=CET_TZ), tick=False) def test_get_bytes_from_time() -> None: """Test the command to set the time.""" - assert LYWSD02(BLEDevice("E7:2E:00:B1:38:96")).get_bytes_from_time(time()) == bytes( - [0xAC, 0xAE, 0xB9, 0x63, 0x01] - ) + assert LYWSD02(BLEDevice("E7:2E:00:B1:38:96", "", {}, -67)).get_bytes_from_time( + time(), + ) == bytes([0xAC, 0xAE, 0xB9, 0x63, 0x01])