From 2a49b6ca7e8d027c02d46336d0ca24f1b5ab30af Mon Sep 17 00:00:00 2001 From: Olen Date: Fri, 22 Sep 2023 14:42:17 +0200 Subject: [PATCH] Add more august actions (#100667) --- homeassistant/components/august/const.py | 4 + homeassistant/components/august/manifest.json | 2 +- homeassistant/components/august/sensor.py | 20 +++ requirements_all.txt | 2 +- requirements_test_all.txt | 2 +- .../get_activity.lock_from_manual.json | 39 +++++ .../get_activity.unlock_from_manual.json | 39 +++++ .../get_activity.unlock_from_tag.json | 39 +++++ tests/components/august/test_sensor.py | 136 +++++++++++++++++- 9 files changed, 278 insertions(+), 5 deletions(-) create mode 100644 tests/components/august/fixtures/get_activity.lock_from_manual.json create mode 100644 tests/components/august/fixtures/get_activity.unlock_from_manual.json create mode 100644 tests/components/august/fixtures/get_activity.unlock_from_tag.json diff --git a/homeassistant/components/august/const.py b/homeassistant/components/august/const.py index 752499e29e2..b97890d09b6 100644 --- a/homeassistant/components/august/const.py +++ b/homeassistant/components/august/const.py @@ -26,12 +26,16 @@ DOMAIN = "august" OPERATION_METHOD_AUTORELOCK = "autorelock" OPERATION_METHOD_REMOTE = "remote" OPERATION_METHOD_KEYPAD = "keypad" +OPERATION_METHOD_MANUAL = "manual" +OPERATION_METHOD_TAG = "tag" OPERATION_METHOD_MOBILE_DEVICE = "mobile" ATTR_OPERATION_AUTORELOCK = "autorelock" ATTR_OPERATION_METHOD = "method" ATTR_OPERATION_REMOTE = "remote" ATTR_OPERATION_KEYPAD = "keypad" +ATTR_OPERATION_MANUAL = "manual" +ATTR_OPERATION_TAG = "tag" # Limit battery, online, and hardware updates to hourly # in order to reduce the number of api requests and diff --git a/homeassistant/components/august/manifest.json b/homeassistant/components/august/manifest.json index c5a0da71136..2fe7d62ac3d 100644 --- a/homeassistant/components/august/manifest.json +++ b/homeassistant/components/august/manifest.json @@ -28,5 +28,5 @@ "documentation": "https://www.home-assistant.io/integrations/august", "iot_class": "cloud_push", "loggers": ["pubnub", "yalexs"], - "requirements": ["yalexs==1.9.0", "yalexs-ble==2.3.0"] + "requirements": ["yalexs==1.10.0", "yalexs-ble==2.3.0"] } diff --git a/homeassistant/components/august/sensor.py b/homeassistant/components/august/sensor.py index 12ed3a88558..75e8cd8984c 100644 --- a/homeassistant/components/august/sensor.py +++ b/homeassistant/components/august/sensor.py @@ -33,13 +33,17 @@ from . import AugustData from .const import ( ATTR_OPERATION_AUTORELOCK, ATTR_OPERATION_KEYPAD, + ATTR_OPERATION_MANUAL, ATTR_OPERATION_METHOD, ATTR_OPERATION_REMOTE, + ATTR_OPERATION_TAG, DOMAIN, OPERATION_METHOD_AUTORELOCK, OPERATION_METHOD_KEYPAD, + OPERATION_METHOD_MANUAL, OPERATION_METHOD_MOBILE_DEVICE, OPERATION_METHOD_REMOTE, + OPERATION_METHOD_TAG, ) from .entity import AugustEntityMixin @@ -183,6 +187,8 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity): self._device = device self._operated_remote = None self._operated_keypad = None + self._operated_manual = None + self._operated_tag = None self._operated_autorelock = None self._operated_time = None self._attr_unique_id = f"{self._device_id}_lock_operator" @@ -200,6 +206,8 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity): self._attr_native_value = lock_activity.operated_by self._operated_remote = lock_activity.operated_remote self._operated_keypad = lock_activity.operated_keypad + self._operated_manual = lock_activity.operated_manual + self._operated_tag = lock_activity.operated_tag self._operated_autorelock = lock_activity.operated_autorelock self._attr_entity_picture = lock_activity.operator_thumbnail_url @@ -212,6 +220,10 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity): attributes[ATTR_OPERATION_REMOTE] = self._operated_remote if self._operated_keypad is not None: attributes[ATTR_OPERATION_KEYPAD] = self._operated_keypad + if self._operated_manual is not None: + attributes[ATTR_OPERATION_MANUAL] = self._operated_manual + if self._operated_tag is not None: + attributes[ATTR_OPERATION_TAG] = self._operated_tag if self._operated_autorelock is not None: attributes[ATTR_OPERATION_AUTORELOCK] = self._operated_autorelock @@ -219,6 +231,10 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity): attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_REMOTE elif self._operated_keypad: attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_KEYPAD + elif self._operated_manual: + attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_MANUAL + elif self._operated_tag: + attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_TAG elif self._operated_autorelock: attributes[ATTR_OPERATION_METHOD] = OPERATION_METHOD_AUTORELOCK else: @@ -241,6 +257,10 @@ class AugustOperatorSensor(AugustEntityMixin, RestoreEntity, SensorEntity): self._operated_remote = last_state.attributes[ATTR_OPERATION_REMOTE] if ATTR_OPERATION_KEYPAD in last_state.attributes: self._operated_keypad = last_state.attributes[ATTR_OPERATION_KEYPAD] + if ATTR_OPERATION_MANUAL in last_state.attributes: + self._operated_manual = last_state.attributes[ATTR_OPERATION_MANUAL] + if ATTR_OPERATION_TAG in last_state.attributes: + self._operated_tag = last_state.attributes[ATTR_OPERATION_TAG] if ATTR_OPERATION_AUTORELOCK in last_state.attributes: self._operated_autorelock = last_state.attributes[ATTR_OPERATION_AUTORELOCK] diff --git a/requirements_all.txt b/requirements_all.txt index aadc9698915..1b2bdde464c 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -2749,7 +2749,7 @@ yalesmartalarmclient==0.3.9 yalexs-ble==2.3.0 # homeassistant.components.august -yalexs==1.9.0 +yalexs==1.10.0 # homeassistant.components.yeelight yeelight==0.7.13 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 0bc99890587..010bc77640d 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -2040,7 +2040,7 @@ yalesmartalarmclient==0.3.9 yalexs-ble==2.3.0 # homeassistant.components.august -yalexs==1.9.0 +yalexs==1.10.0 # homeassistant.components.yeelight yeelight==0.7.13 diff --git a/tests/components/august/fixtures/get_activity.lock_from_manual.json b/tests/components/august/fixtures/get_activity.lock_from_manual.json new file mode 100644 index 00000000000..e2fc195cfda --- /dev/null +++ b/tests/components/august/fixtures/get_activity.lock_from_manual.json @@ -0,0 +1,39 @@ +[ + { + "entities": { + "activity": "mockActivity2", + "house": "123", + "device": "online_with_doorsense", + "callingUser": "mockUserId2", + "otherUser": "deleted" + }, + "callingUser": { + "LastName": "elven princess", + "UserID": "mockUserId2", + "FirstName": "Your favorite" + }, + "otherUser": { + "LastName": "User", + "UserName": "deleteduser", + "FirstName": "Unknown", + "UserID": "deleted", + "PhoneNo": "deleted" + }, + "deviceType": "lock", + "deviceName": "MockHouseTDoor", + "action": "lock", + "dateTime": 1582007218000, + "info": { + "remote": false, + "keypad": false, + "manual": true, + "tag": false, + "DateLogActionID": "ABC+Time" + }, + "deviceID": "online_with_doorsense", + "house": { + "houseName": "MockHouse", + "houseID": "123" + } + } +] diff --git a/tests/components/august/fixtures/get_activity.unlock_from_manual.json b/tests/components/august/fixtures/get_activity.unlock_from_manual.json new file mode 100644 index 00000000000..e8bf95818ce --- /dev/null +++ b/tests/components/august/fixtures/get_activity.unlock_from_manual.json @@ -0,0 +1,39 @@ +[ + { + "entities": { + "activity": "mockActivity2", + "house": "123", + "device": "online_with_doorsense", + "callingUser": "mockUserId2", + "otherUser": "deleted" + }, + "callingUser": { + "LastName": "elven princess", + "UserID": "mockUserId2", + "FirstName": "Your favorite" + }, + "otherUser": { + "LastName": "User", + "UserName": "deleteduser", + "FirstName": "Unknown", + "UserID": "deleted", + "PhoneNo": "deleted" + }, + "deviceType": "lock", + "deviceName": "MockHouseTDoor", + "action": "unlock", + "dateTime": 1582007218000, + "info": { + "remote": false, + "keypad": false, + "manual": true, + "tag": false, + "DateLogActionID": "ABC+Time" + }, + "deviceID": "online_with_doorsense", + "house": { + "houseName": "MockHouse", + "houseID": "123" + } + } +] diff --git a/tests/components/august/fixtures/get_activity.unlock_from_tag.json b/tests/components/august/fixtures/get_activity.unlock_from_tag.json new file mode 100644 index 00000000000..57876428677 --- /dev/null +++ b/tests/components/august/fixtures/get_activity.unlock_from_tag.json @@ -0,0 +1,39 @@ +[ + { + "entities": { + "activity": "mockActivity2", + "house": "123", + "device": "online_with_doorsense", + "callingUser": "mockUserId2", + "otherUser": "deleted" + }, + "callingUser": { + "LastName": "elven princess", + "UserID": "mockUserId2", + "FirstName": "Your favorite" + }, + "otherUser": { + "LastName": "User", + "UserName": "deleteduser", + "FirstName": "Unknown", + "UserID": "deleted", + "PhoneNo": "deleted" + }, + "deviceType": "lock", + "deviceName": "MockHouseTDoor", + "action": "unlock", + "dateTime": 1582007218000, + "info": { + "remote": false, + "keypad": false, + "manual": false, + "tag": true, + "DateLogActionID": "ABC+Time" + }, + "deviceID": "online_with_doorsense", + "house": { + "houseName": "MockHouse", + "houseID": "123" + } + } +] diff --git a/tests/components/august/test_sensor.py b/tests/components/august/test_sensor.py index 7987ab88b1e..7877a9268a4 100644 --- a/tests/components/august/test_sensor.py +++ b/tests/components/august/test_sensor.py @@ -1,6 +1,14 @@ """The sensor tests for the august platform.""" -from homeassistant.const import ATTR_UNIT_OF_MEASUREMENT, PERCENTAGE, STATE_UNKNOWN -from homeassistant.core import HomeAssistant +from typing import Any + +from homeassistant import core as ha +from homeassistant.const import ( + ATTR_ENTITY_PICTURE, + ATTR_UNIT_OF_MEASUREMENT, + PERCENTAGE, + STATE_UNKNOWN, +) +from homeassistant.core import CoreState, HomeAssistant from homeassistant.helpers import entity_registry as er from .mocks import ( @@ -11,6 +19,8 @@ from .mocks import ( _mock_lock_from_fixture, ) +from tests.common import mock_restore_cache_with_extra_data + async def test_create_doorbell(hass: HomeAssistant) -> None: """Test creation of a doorbell.""" @@ -255,6 +265,30 @@ async def test_lock_operator_remote(hass: HomeAssistant) -> None: ) +async def test_lock_operator_manual(hass: HomeAssistant) -> None: + """Test operation of a lock with doorsense and bridge.""" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + + activities = await _mock_activities_from_fixture( + hass, "get_activity.lock_from_manual.json" + ) + await _create_august_with_devices(hass, [lock_one], activities=activities) + + entity_registry = er.async_get(hass) + lock_operator_sensor = entity_registry.async_get( + "sensor.online_with_doorsense_name_operator" + ) + assert lock_operator_sensor + state = hass.states.get("sensor.online_with_doorsense_name_operator") + assert state.state == "Your favorite elven princess" + assert state.attributes["manual"] is True + assert state.attributes["tag"] is False + assert state.attributes["remote"] is False + assert state.attributes["keypad"] is False + assert state.attributes["autorelock"] is False + assert state.attributes["method"] == "manual" + + async def test_lock_operator_autorelock(hass: HomeAssistant) -> None: """Test operation of a lock with doorsense and bridge.""" lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) @@ -297,3 +331,101 @@ async def test_lock_operator_autorelock(hass: HomeAssistant) -> None: ] == "autorelock" ) + + +async def test_unlock_operator_manual(hass: HomeAssistant) -> None: + """Test operation of a lock manually.""" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + + activities = await _mock_activities_from_fixture( + hass, "get_activity.unlock_from_manual.json" + ) + await _create_august_with_devices(hass, [lock_one], activities=activities) + + entity_registry = er.async_get(hass) + lock_operator_sensor = entity_registry.async_get( + "sensor.online_with_doorsense_name_operator" + ) + assert lock_operator_sensor + + state = hass.states.get("sensor.online_with_doorsense_name_operator") + assert state.state == "Your favorite elven princess" + assert state.attributes["manual"] is True + assert state.attributes["tag"] is False + assert state.attributes["remote"] is False + assert state.attributes["keypad"] is False + assert state.attributes["autorelock"] is False + assert state.attributes["method"] == "manual" + + +async def test_unlock_operator_tag(hass: HomeAssistant) -> None: + """Test operation of a lock with a tag.""" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + + activities = await _mock_activities_from_fixture( + hass, "get_activity.unlock_from_tag.json" + ) + await _create_august_with_devices(hass, [lock_one], activities=activities) + + entity_registry = er.async_get(hass) + lock_operator_sensor = entity_registry.async_get( + "sensor.online_with_doorsense_name_operator" + ) + assert lock_operator_sensor + + state = hass.states.get("sensor.online_with_doorsense_name_operator") + assert state.state == "Your favorite elven princess" + assert state.attributes["manual"] is False + assert state.attributes["tag"] is True + assert state.attributes["remote"] is False + assert state.attributes["keypad"] is False + assert state.attributes["autorelock"] is False + assert state.attributes["method"] == "tag" + + +async def test_restored_state( + hass: HomeAssistant, hass_storage: dict[str, Any] +) -> None: + """Test restored state.""" + + entity_id = "sensor.online_with_doorsense_name_operator" + lock_one = await _mock_doorsense_enabled_august_lock_detail(hass) + + fake_state = ha.State( + entity_id, + state="Tag Unlock", + attributes={ + "method": "tag", + "manual": False, + "remote": False, + "keypad": False, + "tag": True, + "autorelock": False, + ATTR_ENTITY_PICTURE: "image.png", + }, + ) + + # Home assistant is not running yet + hass.state = CoreState.not_running + last_reset = "2023-09-22T00:00:00.000000+00:00" + mock_restore_cache_with_extra_data( + hass, + [ + ( + fake_state, + { + "last_reset": last_reset, + }, + ) + ], + ) + + august_entry = await _create_august_with_devices(hass, [lock_one]) + august_entry.add_to_hass(hass) + + await hass.async_block_till_done() + + state = hass.states.get(entity_id) + assert state.state == "Tag Unlock" + assert state.attributes["method"] == "tag" + assert state.attributes[ATTR_ENTITY_PICTURE] == "image.png"