From 284fe091ce59a8564e68f6f1c2e55afe2c90bb2b Mon Sep 17 00:00:00 2001 From: Chris Dohmen Date: Wed, 15 Jan 2025 07:42:09 -0500 Subject: [PATCH] Fix for locks * Update to aiohubspace that fixes an issue around locks being managed Sem-Ver: bugfix --- custom_components/hubspace/entity.py | 9 ++-- custom_components/hubspace/lock.py | 4 +- custom_components/hubspace/manifest.json | 2 +- tests/test_lock.py | 66 +++++++++++++++++++++--- 4 files changed, 67 insertions(+), 14 deletions(-) diff --git a/custom_components/hubspace/entity.py b/custom_components/hubspace/entity.py index ded33fc..2dc162d 100644 --- a/custom_components/hubspace/entity.py +++ b/custom_components/hubspace/entity.py @@ -42,14 +42,13 @@ def __init__( self.bridge = bridge self.controller = controller self.resource = resource - self.device = controller.get_device(resource.id) self.logger = bridge.logger.getChild(resource.type.value) # Entity class attributes unique_id = f"{resource.id}.{instance}" if instance else resource.id self._attr_unique_id = unique_id or resource.id self._attr_has_entity_name = ( - True if self.device.device_information.name else False + True if self.resource.device_information.name else False ) if instance is not False: @@ -66,7 +65,7 @@ async def async_added_to_hass(self) -> None: self.async_on_remove( self.controller.subscribe( self._handle_event, - self.device.id, + self.resource.id, EventType.RESOURCE_UPDATED, ) ) @@ -75,9 +74,9 @@ async def async_added_to_hass(self) -> None: def available(self) -> bool: """Return entity availability.""" # entities without a device attached should be always available - if self.device is None: + if self.resource is None: return True - return self.device.available + return self.resource.available @callback def on_update(self) -> None: diff --git a/custom_components/hubspace/lock.py b/custom_components/hubspace/lock.py index e86ae61..958ea95 100644 --- a/custom_components/hubspace/lock.py +++ b/custom_components/hubspace/lock.py @@ -56,7 +56,7 @@ async def async_unlock(self, **kwargs) -> None: await self.bridge.async_request_call( self.controller.set_state, device_id=self.resource.id, - lock_position=features.CurrentPositionEnum.LOCKING, + lock_position=features.CurrentPositionEnum.UNLOCKING, ) @update_decorator @@ -65,7 +65,7 @@ async def async_lock(self, **kwargs) -> None: await self.bridge.async_request_call( self.controller.set_state, device_id=self.resource.id, - lock_position=features.CurrentPositionEnum.UNLOCKING, + lock_position=features.CurrentPositionEnum.LOCKING, ) diff --git a/custom_components/hubspace/manifest.json b/custom_components/hubspace/manifest.json index eee255f..e3c9df6 100644 --- a/custom_components/hubspace/manifest.json +++ b/custom_components/hubspace/manifest.json @@ -8,6 +8,6 @@ "iot_class": "cloud_polling", "issue_tracker": "https://github.com/jdeath/Hubspace-Homeassistant/issues", "loggers": ["aiohubspace"], - "requirements": ["aiohubspace==0.6.2", "aiofiles==24.1.0"], + "requirements": ["aiohubspace==0.6.4", "aiofiles==24.1.0"], "version": "4.1.0" } diff --git a/tests/test_lock.py b/tests/test_lock.py index 8c2b7ee..94cc8de 100644 --- a/tests/test_lock.py +++ b/tests/test_lock.py @@ -1,7 +1,9 @@ import pytest +from aiohubspace.v1.controllers.lock import features +from aiohubspace.v1.device import HubspaceState from homeassistant.helpers import entity_registry as er -from .utils import create_devices_from_data +from .utils import create_devices_from_data, modify_state lock_tbd = create_devices_from_data("door-lock-TBD.json") lock_tbd_instance = lock_tbd[0] @@ -9,7 +11,7 @@ @pytest.fixture -async def transformer_entity(mocked_entry): +async def lock_entity(mocked_entry): hass, entry, bridge = mocked_entry await bridge.locks.initialize_elem(lock_tbd_instance) await bridge.devices.initialize_elem(lock_tbd_instance) @@ -48,8 +50,8 @@ async def test_async_setup_entry(dev, expected_entities, mocked_entry): @pytest.mark.asyncio -async def test_unlock(transformer_entity): - hass, entry, bridge = transformer_entity +async def test_unlock(lock_entity): + hass, entry, bridge = lock_entity await hass.services.async_call( "lock", "unlock", @@ -60,11 +62,37 @@ async def test_unlock(transformer_entity): assert update_call.args[0] == "put" payload = update_call.kwargs["json"] assert payload["metadeviceId"] == lock_tbd_instance.id + update = payload["values"][0] + assert update["functionClass"] == "lock-control" + assert update["functionInstance"] is None + assert update["value"] == "unlocking" + # Now generate update event by emitting the json we've sent as incoming event + lock_update = create_devices_from_data("door-lock-TBD.json")[0] + modify_state( + lock_update, + HubspaceState( + functionClass="lock-control", + functionInstance=None, + value="unlocking", + ), + ) + event = { + "type": "update", + "device_id": lock_update.id, + "device": lock_update, + } + bridge.emit_event("update", event) + await hass.async_block_till_done() + assert ( + bridge.locks._items[lock_update.id].position.position + == features.CurrentPositionEnum.UNLOCKING + ) + assert hass.states.get(lock_id).state == "opening" @pytest.mark.asyncio -async def test_lock(transformer_entity): - hass, entry, bridge = transformer_entity +async def test_lock(lock_entity): + hass, entry, bridge = lock_entity await hass.services.async_call( "lock", "lock", @@ -75,3 +103,29 @@ async def test_lock(transformer_entity): assert update_call.args[0] == "put" payload = update_call.kwargs["json"] assert payload["metadeviceId"] == lock_tbd_instance.id + update = payload["values"][0] + assert update["functionClass"] == "lock-control" + assert update["functionInstance"] is None + assert update["value"] == "locking" + # Now generate update event by emitting the json we've sent as incoming event + lock_update = create_devices_from_data("door-lock-TBD.json")[0] + modify_state( + lock_update, + HubspaceState( + functionClass="lock-control", + functionInstance=None, + value="locking", + ), + ) + event = { + "type": "update", + "device_id": lock_update.id, + "device": lock_update, + } + bridge.emit_event("update", event) + await hass.async_block_till_done() + assert ( + bridge.locks._items[lock_update.id].position.position + == features.CurrentPositionEnum.LOCKING + ) + assert hass.states.get(lock_id).state == "locking"