From 2c7250fbd06214d156d9308b43c3b26aa9072e74 Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Mon, 30 Dec 2024 17:29:25 +0100 Subject: [PATCH 1/8] switch to char handle --- pylamarzocco/clients/bluetooth.py | 8 ++++---- pylamarzocco/const.py | 4 ++-- pyproject.toml | 2 +- tests/test_bluetooth.py | 12 ++++++------ 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 1a018d4..314f6e2 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -108,7 +108,7 @@ async def set_temp(self, boiler: BoilerType, temperature: float) -> None: await self._write_bluetooth_json_message(data) async def _write_bluetooth_message( - self, characteristic: str, message: bytes | str + self, characteristic: str | int, message: bytes | str ) -> None: """Connect to machine and write a message.""" @@ -135,7 +135,7 @@ async def authenticate() -> None: await client.write_gatt_char( char_specifier=AUTH_CHARACTERISTIC, data=auth_string, - response=False, + response=True, ) except (BleakError, TimeoutError) as e: raise BluetoothConnectionFailed( @@ -151,13 +151,13 @@ async def authenticate() -> None: await client.write_gatt_char( char_specifier=characteristic, data=message, - response=False, + response=True, ) async def _write_bluetooth_json_message( self, data: dict[str, Any], - characteristic: str = SETTINGS_CHARACTERISTIC, + characteristic: str | int = SETTINGS_CHARACTERISTIC, ) -> None: """Write a json message to the machine.""" diff --git a/pylamarzocco/const.py b/pylamarzocco/const.py index b6c3d64..50b8a0d 100644 --- a/pylamarzocco/const.py +++ b/pylamarzocco/const.py @@ -103,5 +103,5 @@ class SmartStandbyMode(StrEnum): # bluetooth BT_MODEL_PREFIXES: Final = ("MICRA", "MINI", "GS3") -SETTINGS_CHARACTERISTIC: Final = "050b7847-e12b-09a8-b04b-8e0922a9abab" -AUTH_CHARACTERISTIC: Final = "090b7847-e12b-09a8-b04b-8e0922a9abab" +SETTINGS_CHARACTERISTIC: Final = 21 # "050b7847-e12b-09a8-b04b-8e0922a9abab" +AUTH_CHARACTERISTIC: Final = 19 # "090b7847-e12b-09a8-b04b-8e0922a9abab" diff --git a/pyproject.toml b/pyproject.toml index 46b7ad4..f51881b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.5" +version = "1.4.6a1" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" diff --git a/tests/test_bluetooth.py b/tests/test_bluetooth.py index 1a53fe0..605d0f1 100644 --- a/tests/test_bluetooth.py +++ b/tests/test_bluetooth.py @@ -21,13 +21,13 @@ async def test_bluetooth_set_power( mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] char_specifier=AUTH_CHARACTERISTIC, data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=False, + response=True, ) mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] char_specifier=SETTINGS_CHARACTERISTIC, data=b'{"name":"MachineChangeMode","parameter":{"mode":"BrewingMode"}}\x00', - response=False, + response=True, ) @@ -41,13 +41,13 @@ async def test_bluetooth_set_steam( mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] char_specifier=AUTH_CHARACTERISTIC, data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=False, + response=True, ) mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] char_specifier=SETTINGS_CHARACTERISTIC, data=b'{"name":"SettingBoilerEnable","parameter":{"identifier":"SteamBoiler","state":true}}\x00', - response=False, + response=True, ) @@ -61,11 +61,11 @@ async def test_bluetooth_set_temperature( mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] char_specifier=AUTH_CHARACTERISTIC, data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=False, + response=True, ) mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] char_specifier=SETTINGS_CHARACTERISTIC, data=b'{"name":"SettingBoilerTarget","parameter":{"identifier":"SteamBoiler","value":131}}\x00', - response=False, + response=True, ) From 4bf7d573a1e77596af0442aca2974a98ce47ea85 Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:05:13 +0100 Subject: [PATCH 2/8] switch to char handle --- pylamarzocco/const.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pylamarzocco/const.py b/pylamarzocco/const.py index 50b8a0d..b6c3d64 100644 --- a/pylamarzocco/const.py +++ b/pylamarzocco/const.py @@ -103,5 +103,5 @@ class SmartStandbyMode(StrEnum): # bluetooth BT_MODEL_PREFIXES: Final = ("MICRA", "MINI", "GS3") -SETTINGS_CHARACTERISTIC: Final = 21 # "050b7847-e12b-09a8-b04b-8e0922a9abab" -AUTH_CHARACTERISTIC: Final = 19 # "090b7847-e12b-09a8-b04b-8e0922a9abab" +SETTINGS_CHARACTERISTIC: Final = "050b7847-e12b-09a8-b04b-8e0922a9abab" +AUTH_CHARACTERISTIC: Final = "090b7847-e12b-09a8-b04b-8e0922a9abab" From 33b998e0ca3939d4d55a9f7ffc94f53144f9c96c Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Mon, 30 Dec 2024 20:05:31 +0100 Subject: [PATCH 3/8] switch to char handle --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index f51881b..3628cbf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a1" +version = "1.4.6a2" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" From 3a8a291a4a507cf8bff6e88e7aa5f6d1288ec5fb Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Tue, 31 Dec 2024 07:33:09 +0100 Subject: [PATCH 4/8] log services --- pylamarzocco/clients/bluetooth.py | 31 +++++++++++++++++++++++++++++++ pyproject.toml | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 314f6e2..4c91de7 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -120,6 +120,37 @@ async def _write_bluetooth_message( message += b"\x00" async with BleakClient(self._address_or_ble_device) as client: + for service in client.services: + _logger.warning("[Service] %s", service) + + for char in service.characteristics: + if "read" in char.properties: + try: + value = await client.read_gatt_char(char.uuid) + extra = f", Value: {value}" + except Exception as e: + extra = f", Error: {e}" + else: + extra = "" + + if "write-without-response" in char.properties: + extra += f", Max write w/o rsp size: {char.max_write_without_response_size}" + + _logger.warning( + " [Characteristic] %s (%s)%s", + char, + ",".join(char.properties), + extra, + ) + + for descriptor in char.descriptors: + try: + value = await client.read_gatt_descriptor(descriptor.handle) + _logger.warning( + " [Descriptor] %s, Value: %r", descriptor, value + ) + except Exception as e: + _logger.error(" [Descriptor] %s, Error: %s", descriptor, e) async def authenticate() -> None: """Build authentication string and send it to the machine.""" diff --git a/pyproject.toml b/pyproject.toml index 3628cbf..ab60090 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a2" +version = "1.4.6a3" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" From 0222759aff922dd786a6e7af7c3b3a4ce86f358d Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Tue, 31 Dec 2024 08:59:51 +0100 Subject: [PATCH 5/8] fixed chars --- pylamarzocco/clients/bluetooth.py | 6 ++++++ pylamarzocco/const.py | 4 ++-- pyproject.toml | 2 +- 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 4c91de7..446c391 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -151,6 +151,12 @@ async def _write_bluetooth_message( ) except Exception as e: _logger.error(" [Descriptor] %s, Error: %s", descriptor, e) + for service in client.services: + for char in service.characteristics: + if char.uuid == "090b7847-e12b-09a8-b04b-8e0922a9abab": + _logger.warning("Auth char: %s", char.handle) + if char.uuid == "050b7847-e12b-09a8-b04b-8e0922a9abab": + _logger.warning("Settings char: %s", char.handle) async def authenticate() -> None: """Build authentication string and send it to the machine.""" diff --git a/pylamarzocco/const.py b/pylamarzocco/const.py index b6c3d64..f578460 100644 --- a/pylamarzocco/const.py +++ b/pylamarzocco/const.py @@ -103,5 +103,5 @@ class SmartStandbyMode(StrEnum): # bluetooth BT_MODEL_PREFIXES: Final = ("MICRA", "MINI", "GS3") -SETTINGS_CHARACTERISTIC: Final = "050b7847-e12b-09a8-b04b-8e0922a9abab" -AUTH_CHARACTERISTIC: Final = "090b7847-e12b-09a8-b04b-8e0922a9abab" +SETTINGS_CHARACTERISTIC: Final = 22 # "050b7847-e12b-09a8-b04b-8e0922a9abab" +AUTH_CHARACTERISTIC: Final = 20 # "090b7847-e12b-09a8-b04b-8e0922a9abab" diff --git a/pyproject.toml b/pyproject.toml index ab60090..f5150cc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a3" +version = "1.4.6a4" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" From 027024a92950300685ac9e9e6b28a635ad989f9d Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Tue, 31 Dec 2024 09:13:09 +0100 Subject: [PATCH 6/8] find chars --- pylamarzocco/clients/bluetooth.py | 55 +++++--------- pylamarzocco/const.py | 4 +- pyproject.toml | 2 +- tests/test_bluetooth.py | 114 +++++++++++++++--------------- 4 files changed, 77 insertions(+), 98 deletions(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 446c391..28c88df 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -15,6 +15,7 @@ BleakScanner, BLEDevice, ) +from bleak.backends.characteristic import BleakGATTCharacteristic from pylamarzocco.const import ( AUTH_CHARACTERISTIC, @@ -108,7 +109,7 @@ async def set_temp(self, boiler: BoilerType, temperature: float) -> None: await self._write_bluetooth_json_message(data) async def _write_bluetooth_message( - self, characteristic: str | int, message: bytes | str + self, characteristic: str, message: bytes | str ) -> None: """Connect to machine and write a message.""" @@ -120,43 +121,21 @@ async def _write_bluetooth_message( message += b"\x00" async with BleakClient(self._address_or_ble_device) as client: - for service in client.services: - _logger.warning("[Service] %s", service) - for char in service.characteristics: - if "read" in char.properties: - try: - value = await client.read_gatt_char(char.uuid) - extra = f", Value: {value}" - except Exception as e: - extra = f", Error: {e}" - else: - extra = "" - - if "write-without-response" in char.properties: - extra += f", Max write w/o rsp size: {char.max_write_without_response_size}" - - _logger.warning( - " [Characteristic] %s (%s)%s", - char, - ",".join(char.properties), - extra, - ) - - for descriptor in char.descriptors: - try: - value = await client.read_gatt_descriptor(descriptor.handle) - _logger.warning( - " [Descriptor] %s, Value: %r", descriptor, value - ) - except Exception as e: - _logger.error(" [Descriptor] %s, Error: %s", descriptor, e) + auth_char: BleakGATTCharacteristic | None = None + settings_char: BleakGATTCharacteristic | None = None for service in client.services: for char in service.characteristics: - if char.uuid == "090b7847-e12b-09a8-b04b-8e0922a9abab": - _logger.warning("Auth char: %s", char.handle) - if char.uuid == "050b7847-e12b-09a8-b04b-8e0922a9abab": - _logger.warning("Settings char: %s", char.handle) + if char.uuid == AUTH_CHARACTERISTIC: + auth_char = char + _logger.debug("Found auth characteristic: %s", char.handle) + if char.uuid == characteristic: + settings_char = char + _logger.debug("Found settings characteristic: %s", char.handle) + if auth_char is None or settings_char is None: + raise BluetoothConnectionFailed( + "Could not find required characteristics on machine." + ) async def authenticate() -> None: """Build authentication string and send it to the machine.""" @@ -170,7 +149,7 @@ async def authenticate() -> None: try: await client.write_gatt_char( - char_specifier=AUTH_CHARACTERISTIC, + char_specifier=auth_char, data=auth_string, response=True, ) @@ -186,7 +165,7 @@ async def authenticate() -> None: ) await client.write_gatt_char( - char_specifier=characteristic, + char_specifier=settings_char, data=message, response=True, ) @@ -194,7 +173,7 @@ async def authenticate() -> None: async def _write_bluetooth_json_message( self, data: dict[str, Any], - characteristic: str | int = SETTINGS_CHARACTERISTIC, + characteristic: str = SETTINGS_CHARACTERISTIC, ) -> None: """Write a json message to the machine.""" diff --git a/pylamarzocco/const.py b/pylamarzocco/const.py index f578460..b6c3d64 100644 --- a/pylamarzocco/const.py +++ b/pylamarzocco/const.py @@ -103,5 +103,5 @@ class SmartStandbyMode(StrEnum): # bluetooth BT_MODEL_PREFIXES: Final = ("MICRA", "MINI", "GS3") -SETTINGS_CHARACTERISTIC: Final = 22 # "050b7847-e12b-09a8-b04b-8e0922a9abab" -AUTH_CHARACTERISTIC: Final = 20 # "090b7847-e12b-09a8-b04b-8e0922a9abab" +SETTINGS_CHARACTERISTIC: Final = "050b7847-e12b-09a8-b04b-8e0922a9abab" +AUTH_CHARACTERISTIC: Final = "090b7847-e12b-09a8-b04b-8e0922a9abab" diff --git a/pyproject.toml b/pyproject.toml index f5150cc..7b5d1f8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a4" +version = "1.4.6a5" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" diff --git a/tests/test_bluetooth.py b/tests/test_bluetooth.py index 605d0f1..cb29ba3 100644 --- a/tests/test_bluetooth.py +++ b/tests/test_bluetooth.py @@ -1,71 +1,71 @@ -""""Test the Bluetooth client.""" +# """"Test the Bluetooth client.""" -from collections.abc import Generator -from unittest.mock import AsyncMock +# from collections.abc import Generator +# from unittest.mock import AsyncMock -from pylamarzocco.clients.bluetooth import LaMarzoccoBluetoothClient -from pylamarzocco.const import ( - AUTH_CHARACTERISTIC, - SETTINGS_CHARACTERISTIC, - BoilerType, -) +# from pylamarzocco.clients.bluetooth import LaMarzoccoBluetoothClient +# from pylamarzocco.const import ( +# AUTH_CHARACTERISTIC, +# SETTINGS_CHARACTERISTIC, +# BoilerType, +# ) -async def test_bluetooth_set_power( - bluetooth_client: LaMarzoccoBluetoothClient, - mock_bleak: Generator[AsyncMock, None, None], -): - """Test setting the power.""" - await bluetooth_client.set_power(True) +# async def test_bluetooth_set_power( +# bluetooth_client: LaMarzoccoBluetoothClient, +# mock_bleak: Generator[AsyncMock, None, None], +# ): +# """Test setting the power.""" +# await bluetooth_client.set_power(True) - mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] - char_specifier=AUTH_CHARACTERISTIC, - data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=True, - ) +# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] +# char_specifier=AUTH_CHARACTERISTIC, +# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", +# response=True, +# ) - mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] - char_specifier=SETTINGS_CHARACTERISTIC, - data=b'{"name":"MachineChangeMode","parameter":{"mode":"BrewingMode"}}\x00', - response=True, - ) +# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] +# char_specifier=SETTINGS_CHARACTERISTIC, +# data=b'{"name":"MachineChangeMode","parameter":{"mode":"BrewingMode"}}\x00', +# response=True, +# ) -async def test_bluetooth_set_steam( - bluetooth_client: LaMarzoccoBluetoothClient, - mock_bleak: Generator[AsyncMock, None, None], -): - """Test setting the steam.""" - await bluetooth_client.set_steam(True) +# async def test_bluetooth_set_steam( +# bluetooth_client: LaMarzoccoBluetoothClient, +# mock_bleak: Generator[AsyncMock, None, None], +# ): +# """Test setting the steam.""" +# await bluetooth_client.set_steam(True) - mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] - char_specifier=AUTH_CHARACTERISTIC, - data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=True, - ) +# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] +# char_specifier=AUTH_CHARACTERISTIC, +# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", +# response=True, +# ) - mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] - char_specifier=SETTINGS_CHARACTERISTIC, - data=b'{"name":"SettingBoilerEnable","parameter":{"identifier":"SteamBoiler","state":true}}\x00', - response=True, - ) +# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] +# char_specifier=SETTINGS_CHARACTERISTIC, +# data=b'{"name":"SettingBoilerEnable","parameter":{"identifier":"SteamBoiler","state":true}}\x00', +# response=True, +# ) -async def test_bluetooth_set_temperature( - bluetooth_client: LaMarzoccoBluetoothClient, - mock_bleak: Generator[AsyncMock, None, None], -): - """Test setting the temp.""" - await bluetooth_client.set_temp(BoilerType.STEAM, 131) +# async def test_bluetooth_set_temperature( +# bluetooth_client: LaMarzoccoBluetoothClient, +# mock_bleak: Generator[AsyncMock, None, None], +# ): +# """Test setting the temp.""" +# await bluetooth_client.set_temp(BoilerType.STEAM, 131) - mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] - char_specifier=AUTH_CHARACTERISTIC, - data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", - response=True, - ) +# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] +# char_specifier=AUTH_CHARACTERISTIC, +# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", +# response=True, +# ) - mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] - char_specifier=SETTINGS_CHARACTERISTIC, - data=b'{"name":"SettingBoilerTarget","parameter":{"identifier":"SteamBoiler","value":131}}\x00', - response=True, - ) +# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] +# char_specifier=SETTINGS_CHARACTERISTIC, +# data=b'{"name":"SettingBoilerTarget","parameter":{"identifier":"SteamBoiler","value":131}}\x00', +# response=True, +# ) From 51a7cbd2db544da2b693b771f73f60c814980791 Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Tue, 31 Dec 2024 20:58:10 +0100 Subject: [PATCH 7/8] debug get char --- pylamarzocco/clients/bluetooth.py | 44 +++++++++++++++++++++---------- pyproject.toml | 2 +- 2 files changed, 31 insertions(+), 15 deletions(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 28c88df..0065679 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -122,20 +122,12 @@ async def _write_bluetooth_message( async with BleakClient(self._address_or_ble_device) as client: - auth_char: BleakGATTCharacteristic | None = None - settings_char: BleakGATTCharacteristic | None = None - for service in client.services: - for char in service.characteristics: - if char.uuid == AUTH_CHARACTERISTIC: - auth_char = char - _logger.debug("Found auth characteristic: %s", char.handle) - if char.uuid == characteristic: - settings_char = char - _logger.debug("Found settings characteristic: %s", char.handle) - if auth_char is None or settings_char is None: - raise BluetoothConnectionFailed( - "Could not find required characteristics on machine." - ) + auth_c = client.services.get_characteristic(AUTH_CHARACTERISTIC) + if auth_c is not None: + _logger.warning("Found auth characteristic: %s", auth_c.handle) + settings_c = client.services.get_characteristic(SETTINGS_CHARACTERISTIC) + if settings_c is not None: + _logger.warning("Found settings characteristic: %s", settings_c.handle) async def authenticate() -> None: """Build authentication string and send it to the machine.""" @@ -147,6 +139,18 @@ async def authenticate() -> None: base64.b64encode(user_bytes) + b"@" + base64.b64encode(token) ) + auth_char: BleakGATTCharacteristic | None = None + for service in client.services: + for char in service.characteristics: + if char.uuid == AUTH_CHARACTERISTIC: + auth_char = char + _logger.debug("Found auth characteristic: %s", char.handle) + break + if auth_char is None: + raise BluetoothConnectionFailed( + f"Could not find auth characteristic {AUTH_CHARACTERISTIC} on machine." + ) + try: await client.write_gatt_char( char_specifier=auth_char, @@ -164,6 +168,18 @@ async def authenticate() -> None: "Sending bluetooth message: %s to %s", message, characteristic ) + settings_char: BleakGATTCharacteristic | None = None + for service in client.services: + for char in service.characteristics: + if char.uuid == characteristic: + settings_char = char + _logger.debug("Found settings characteristic: %s", char.handle) + break + if settings_char is None: + raise BluetoothConnectionFailed( + f"Could not find characteristic {characteristic} on machine." + ) + await client.write_gatt_char( char_specifier=settings_char, data=message, diff --git a/pyproject.toml b/pyproject.toml index 7b5d1f8..3460d6b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a5" +version = "1.4.6a6" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" From 98f7921733b48c6ba9dcfd48d0335d0e6e7420ef Mon Sep 17 00:00:00 2001 From: Josef Zweck <24647999+zweckj@users.noreply.github.com> Date: Tue, 31 Dec 2024 21:50:49 +0100 Subject: [PATCH 8/8] more fixes --- pylamarzocco/clients/bluetooth.py | 38 +++------- pyproject.toml | 2 +- tests/conftest.py | 2 + tests/test_bluetooth.py | 114 +++++++++++++++--------------- 4 files changed, 71 insertions(+), 85 deletions(-) diff --git a/pylamarzocco/clients/bluetooth.py b/pylamarzocco/clients/bluetooth.py index 0065679..40557c5 100644 --- a/pylamarzocco/clients/bluetooth.py +++ b/pylamarzocco/clients/bluetooth.py @@ -15,7 +15,6 @@ BleakScanner, BLEDevice, ) -from bleak.backends.characteristic import BleakGATTCharacteristic from pylamarzocco.const import ( AUTH_CHARACTERISTIC, @@ -122,13 +121,6 @@ async def _write_bluetooth_message( async with BleakClient(self._address_or_ble_device) as client: - auth_c = client.services.get_characteristic(AUTH_CHARACTERISTIC) - if auth_c is not None: - _logger.warning("Found auth characteristic: %s", auth_c.handle) - settings_c = client.services.get_characteristic(SETTINGS_CHARACTERISTIC) - if settings_c is not None: - _logger.warning("Found settings characteristic: %s", settings_c.handle) - async def authenticate() -> None: """Build authentication string and send it to the machine.""" @@ -139,21 +131,17 @@ async def authenticate() -> None: base64.b64encode(user_bytes) + b"@" + base64.b64encode(token) ) - auth_char: BleakGATTCharacteristic | None = None - for service in client.services: - for char in service.characteristics: - if char.uuid == AUTH_CHARACTERISTIC: - auth_char = char - _logger.debug("Found auth characteristic: %s", char.handle) - break - if auth_char is None: + auth_characteristic = client.services.get_characteristic( + AUTH_CHARACTERISTIC + ) + if auth_characteristic is None: raise BluetoothConnectionFailed( f"Could not find auth characteristic {AUTH_CHARACTERISTIC} on machine." ) try: await client.write_gatt_char( - char_specifier=auth_char, + char_specifier=auth_characteristic, data=auth_string, response=True, ) @@ -168,20 +156,16 @@ async def authenticate() -> None: "Sending bluetooth message: %s to %s", message, characteristic ) - settings_char: BleakGATTCharacteristic | None = None - for service in client.services: - for char in service.characteristics: - if char.uuid == characteristic: - settings_char = char - _logger.debug("Found settings characteristic: %s", char.handle) - break - if settings_char is None: + settings_characteristic = client.services.get_characteristic( + SETTINGS_CHARACTERISTIC + ) + if settings_characteristic is None: raise BluetoothConnectionFailed( - f"Could not find characteristic {characteristic} on machine." + f"Could not find settings characteristic {SETTINGS_CHARACTERISTIC} on machine." ) await client.write_gatt_char( - char_specifier=settings_char, + char_specifier=settings_characteristic, data=message, response=True, ) diff --git a/pyproject.toml b/pyproject.toml index 3460d6b..5a17142 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "pylamarzocco" -version = "1.4.6a6" +version = "1.4.6" license = { text = "MIT" } description = "A Python implementation of the new La Marzocco API" readme = "README.md" diff --git a/tests/conftest.py b/tests/conftest.py index 03ec488..1cd9afa 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -161,4 +161,6 @@ def mock_bleak() -> Generator[AsyncMock, None, None]: """Mock BleakClient.""" with patch("pylamarzocco.clients.bluetooth.BleakClient") as bleak_client: aenter = bleak_client.return_value.__aenter__.return_value + aenter.services.get_characteristic = lambda uuid: uuid + yield aenter diff --git a/tests/test_bluetooth.py b/tests/test_bluetooth.py index cb29ba3..605d0f1 100644 --- a/tests/test_bluetooth.py +++ b/tests/test_bluetooth.py @@ -1,71 +1,71 @@ -# """"Test the Bluetooth client.""" +""""Test the Bluetooth client.""" -# from collections.abc import Generator -# from unittest.mock import AsyncMock +from collections.abc import Generator +from unittest.mock import AsyncMock -# from pylamarzocco.clients.bluetooth import LaMarzoccoBluetoothClient -# from pylamarzocco.const import ( -# AUTH_CHARACTERISTIC, -# SETTINGS_CHARACTERISTIC, -# BoilerType, -# ) +from pylamarzocco.clients.bluetooth import LaMarzoccoBluetoothClient +from pylamarzocco.const import ( + AUTH_CHARACTERISTIC, + SETTINGS_CHARACTERISTIC, + BoilerType, +) -# async def test_bluetooth_set_power( -# bluetooth_client: LaMarzoccoBluetoothClient, -# mock_bleak: Generator[AsyncMock, None, None], -# ): -# """Test setting the power.""" -# await bluetooth_client.set_power(True) +async def test_bluetooth_set_power( + bluetooth_client: LaMarzoccoBluetoothClient, + mock_bleak: Generator[AsyncMock, None, None], +): + """Test setting the power.""" + await bluetooth_client.set_power(True) -# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] -# char_specifier=AUTH_CHARACTERISTIC, -# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", -# response=True, -# ) + mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] + char_specifier=AUTH_CHARACTERISTIC, + data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", + response=True, + ) -# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] -# char_specifier=SETTINGS_CHARACTERISTIC, -# data=b'{"name":"MachineChangeMode","parameter":{"mode":"BrewingMode"}}\x00', -# response=True, -# ) + mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] + char_specifier=SETTINGS_CHARACTERISTIC, + data=b'{"name":"MachineChangeMode","parameter":{"mode":"BrewingMode"}}\x00', + response=True, + ) -# async def test_bluetooth_set_steam( -# bluetooth_client: LaMarzoccoBluetoothClient, -# mock_bleak: Generator[AsyncMock, None, None], -# ): -# """Test setting the steam.""" -# await bluetooth_client.set_steam(True) +async def test_bluetooth_set_steam( + bluetooth_client: LaMarzoccoBluetoothClient, + mock_bleak: Generator[AsyncMock, None, None], +): + """Test setting the steam.""" + await bluetooth_client.set_steam(True) -# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] -# char_specifier=AUTH_CHARACTERISTIC, -# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", -# response=True, -# ) + mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] + char_specifier=AUTH_CHARACTERISTIC, + data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", + response=True, + ) -# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] -# char_specifier=SETTINGS_CHARACTERISTIC, -# data=b'{"name":"SettingBoilerEnable","parameter":{"identifier":"SteamBoiler","state":true}}\x00', -# response=True, -# ) + mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] + char_specifier=SETTINGS_CHARACTERISTIC, + data=b'{"name":"SettingBoilerEnable","parameter":{"identifier":"SteamBoiler","state":true}}\x00', + response=True, + ) -# async def test_bluetooth_set_temperature( -# bluetooth_client: LaMarzoccoBluetoothClient, -# mock_bleak: Generator[AsyncMock, None, None], -# ): -# """Test setting the temp.""" -# await bluetooth_client.set_temp(BoilerType.STEAM, 131) +async def test_bluetooth_set_temperature( + bluetooth_client: LaMarzoccoBluetoothClient, + mock_bleak: Generator[AsyncMock, None, None], +): + """Test setting the temp.""" + await bluetooth_client.set_temp(BoilerType.STEAM, 131) -# mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] -# char_specifier=AUTH_CHARACTERISTIC, -# data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", -# response=True, -# ) + mock_bleak.write_gatt_char.assert_any_call( # type: ignore[attr-defined] + char_specifier=AUTH_CHARACTERISTIC, + data=b"dXNlcm5hbWU6c2VyaWFs@dG9rZW4=", + response=True, + ) -# mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] -# char_specifier=SETTINGS_CHARACTERISTIC, -# data=b'{"name":"SettingBoilerTarget","parameter":{"identifier":"SteamBoiler","value":131}}\x00', -# response=True, -# ) + mock_bleak.write_gatt_char.assert_called_with( # type: ignore[attr-defined] + char_specifier=SETTINGS_CHARACTERISTIC, + data=b'{"name":"SettingBoilerTarget","parameter":{"identifier":"SteamBoiler","value":131}}\x00', + response=True, + )