From a6742eff34b952929a2516c7d7a7b31dfbae26d0 Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Fri, 11 Feb 2022 17:13:35 -0600 Subject: [PATCH] Add button to wake august locks from deep sleep (#66343) --- homeassistant/components/august/button.py | 39 +++++++++++++++++++++++ homeassistant/components/august/camera.py | 2 -- homeassistant/components/august/const.py | 1 + homeassistant/components/august/entity.py | 5 +++ homeassistant/components/august/lock.py | 7 ---- tests/components/august/mocks.py | 13 ++++++-- tests/components/august/test_button.py | 27 ++++++++++++++++ 7 files changed, 83 insertions(+), 11 deletions(-) create mode 100644 homeassistant/components/august/button.py create mode 100644 tests/components/august/test_button.py diff --git a/homeassistant/components/august/button.py b/homeassistant/components/august/button.py new file mode 100644 index 00000000000..d7f2a5ba4ae --- /dev/null +++ b/homeassistant/components/august/button.py @@ -0,0 +1,39 @@ +"""Support for August buttons.""" +from yalexs.lock import Lock + +from homeassistant.components.button import ButtonEntity +from homeassistant.config_entries import ConfigEntry +from homeassistant.core import HomeAssistant, callback +from homeassistant.helpers.entity_platform import AddEntitiesCallback + +from . import AugustData +from .const import DATA_AUGUST, DOMAIN +from .entity import AugustEntityMixin + + +async def async_setup_entry( + hass: HomeAssistant, + config_entry: ConfigEntry, + async_add_entities: AddEntitiesCallback, +) -> None: + """Set up August lock wake buttons.""" + data: AugustData = hass.data[DOMAIN][config_entry.entry_id][DATA_AUGUST] + async_add_entities([AugustWakeLockButton(data, lock) for lock in data.locks]) + + +class AugustWakeLockButton(AugustEntityMixin, ButtonEntity): + """Representation of an August lock wake button.""" + + def __init__(self, data: AugustData, device: Lock) -> None: + """Initialize the lock wake button.""" + super().__init__(data, device) + self._attr_name = f"{device.device_name} Wake" + self._attr_unique_id = f"{self._device_id}_wake" + + async def async_press(self, **kwargs): + """Wake the device.""" + await self._data.async_status_async(self._device_id, self._hyper_bridge) + + @callback + def _update_from_data(self): + """Nothing to update as buttons are stateless.""" diff --git a/homeassistant/components/august/camera.py b/homeassistant/components/august/camera.py index ce1ede86538..26889427555 100644 --- a/homeassistant/components/august/camera.py +++ b/homeassistant/components/august/camera.py @@ -37,8 +37,6 @@ class AugustCamera(AugustEntityMixin, Camera): def __init__(self, data, device, session, timeout): """Initialize a August security camera.""" super().__init__(data, device) - self._data = data - self._device = device self._timeout = timeout self._session = session self._image_url = None diff --git a/homeassistant/components/august/const.py b/homeassistant/components/august/const.py index ae3fcdf90e3..ac6f463467f 100644 --- a/homeassistant/components/august/const.py +++ b/homeassistant/components/august/const.py @@ -46,6 +46,7 @@ ACTIVITY_UPDATE_INTERVAL = timedelta(seconds=10) LOGIN_METHODS = ["phone", "email"] PLATFORMS = [ + Platform.BUTTON, Platform.CAMERA, Platform.BINARY_SENSOR, Platform.LOCK, diff --git a/homeassistant/components/august/entity.py b/homeassistant/components/august/entity.py index a0fe44838c2..209747da0be 100644 --- a/homeassistant/components/august/entity.py +++ b/homeassistant/components/august/entity.py @@ -36,6 +36,11 @@ class AugustEntityMixin(Entity): def _detail(self): return self._data.get_device_detail(self._device.device_id) + @property + def _hyper_bridge(self): + """Check if the lock has a paired hyper bridge.""" + return bool(self._detail.bridge and self._detail.bridge.hyper_bridge) + @callback def _update_from_data_and_write_state(self): self._update_from_data() diff --git a/homeassistant/components/august/lock.py b/homeassistant/components/august/lock.py index 41abc1c6aa2..e30f8301a8f 100644 --- a/homeassistant/components/august/lock.py +++ b/homeassistant/components/august/lock.py @@ -39,18 +39,11 @@ class AugustLock(AugustEntityMixin, RestoreEntity, LockEntity): def __init__(self, data, device): """Initialize the lock.""" super().__init__(data, device) - self._data = data - self._device = device self._lock_status = None self._attr_name = device.device_name self._attr_unique_id = f"{self._device_id:s}_lock" self._update_from_data() - @property - def _hyper_bridge(self): - """Check if the lock has a paired hyper bridge.""" - return bool(self._detail.bridge and self._detail.bridge.hyper_bridge) - async def async_lock(self, **kwargs): """Lock the device.""" if self._data.activity_stream.pubnub.connected: diff --git a/tests/components/august/mocks.py b/tests/components/august/mocks.py index 2d572b886f3..e419488becc 100644 --- a/tests/components/august/mocks.py +++ b/tests/components/august/mocks.py @@ -75,7 +75,16 @@ async def _mock_setup_august( return entry -async def _create_august_with_devices( # noqa: C901 +async def _create_august_with_devices( + hass, devices, api_call_side_effects=None, activities=None, pubnub=None +): + entry, api_instance = await _create_august_api_with_devices( + hass, devices, api_call_side_effects, activities, pubnub + ) + return entry + + +async def _create_august_api_with_devices( # noqa: C901 hass, devices, api_call_side_effects=None, activities=None, pubnub=None ): if api_call_side_effects is None: @@ -171,7 +180,7 @@ async def _create_august_with_devices( # noqa: C901 # are any locks assert api_instance.async_status_async.mock_calls - return entry + return entry, api_instance async def _mock_setup_august_with_api_side_effects(hass, api_call_side_effects, pubnub): diff --git a/tests/components/august/test_button.py b/tests/components/august/test_button.py new file mode 100644 index 00000000000..2d3e6caf884 --- /dev/null +++ b/tests/components/august/test_button.py @@ -0,0 +1,27 @@ +"""The button tests for the august platform.""" + +from homeassistant.components.button import DOMAIN as BUTTON_DOMAIN +from homeassistant.components.button.const import SERVICE_PRESS +from homeassistant.const import ATTR_ENTITY_ID + +from tests.components.august.mocks import ( + _create_august_api_with_devices, + _mock_lock_from_fixture, +) + + +async def test_wake_lock(hass): + """Test creation of a lock and wake it.""" + lock_one = await _mock_lock_from_fixture( + hass, "get_lock.online_with_doorsense.json" + ) + _, api_instance = await _create_august_api_with_devices(hass, [lock_one]) + entity_id = "button.online_with_doorsense_name_wake" + binary_sensor_online_with_doorsense_name = hass.states.get(entity_id) + assert binary_sensor_online_with_doorsense_name is not None + api_instance.async_status_async.reset_mock() + assert await hass.services.async_call( + BUTTON_DOMAIN, SERVICE_PRESS, {ATTR_ENTITY_ID: entity_id}, blocking=True + ) + await hass.async_block_till_done() + api_instance.async_status_async.assert_called_once()