From 5e07ab17b27d629ad48ace24d6777fc053514a55 Mon Sep 17 00:00:00 2001 From: Robert Svensson Date: Wed, 28 Apr 2021 19:46:15 +0200 Subject: [PATCH] Add support for deCONZ alarm events in logbook (#49652) * Add support for alarm events in logbook * Update homeassistant/components/deconz/alarm_control_panel.py Co-authored-by: jjlawren --- .../components/deconz/alarm_control_panel.py | 3 +- .../components/deconz/deconz_event.py | 6 +- homeassistant/components/deconz/logbook.py | 24 +++++- tests/components/deconz/test_deconz_event.py | 14 +++- tests/components/deconz/test_logbook.py | 84 ++++++++++++++++++- 5 files changed, 123 insertions(+), 8 deletions(-) diff --git a/homeassistant/components/deconz/alarm_control_panel.py b/homeassistant/components/deconz/alarm_control_panel.py index 69a62e14aed..59749c0680d 100644 --- a/homeassistant/components/deconz/alarm_control_panel.py +++ b/homeassistant/components/deconz/alarm_control_panel.py @@ -22,7 +22,6 @@ from homeassistant.const import ( STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, - STATE_UNKNOWN, ) from homeassistant.core import callback from homeassistant.helpers import entity_platform @@ -145,7 +144,7 @@ class DeconzAlarmControlPanel(DeconzDevice, AlarmControlPanelEntity): @property def state(self) -> str: """Return the state of the control panel.""" - return DECONZ_TO_ALARM_STATE.get(self._device.state, STATE_UNKNOWN) + return DECONZ_TO_ALARM_STATE.get(self._device.state) async def async_alarm_arm_away(self, code: None = None) -> None: """Send arm away command.""" diff --git a/homeassistant/components/deconz/deconz_event.py b/homeassistant/components/deconz/deconz_event.py index afb9dd7fc79..872dc3688c2 100644 --- a/homeassistant/components/deconz/deconz_event.py +++ b/homeassistant/components/deconz/deconz_event.py @@ -20,7 +20,6 @@ from homeassistant.const import ( STATE_ALARM_ARMED_HOME, STATE_ALARM_ARMED_NIGHT, STATE_ALARM_DISARMED, - STATE_UNKNOWN, ) from homeassistant.core import callback from homeassistant.helpers.dispatcher import async_dispatcher_connect @@ -170,11 +169,14 @@ class DeconzAlarmEvent(DeconzEvent): state, code, _area = self._device.action.split(",") + if state not in DECONZ_TO_ALARM_STATE: + return + data = { CONF_ID: self.event_id, CONF_UNIQUE_ID: self.serial, CONF_DEVICE_ID: self.device_id, - CONF_EVENT: DECONZ_TO_ALARM_STATE.get(state, STATE_UNKNOWN), + CONF_EVENT: DECONZ_TO_ALARM_STATE[state], CONF_CODE: code, } diff --git a/homeassistant/components/deconz/logbook.py b/homeassistant/components/deconz/logbook.py index e6f3e9362cd..b36e06c0cf6 100644 --- a/homeassistant/components/deconz/logbook.py +++ b/homeassistant/components/deconz/logbook.py @@ -8,7 +8,12 @@ from homeassistant.core import HomeAssistant, callback from homeassistant.helpers.event import Event from .const import CONF_GESTURE, DOMAIN as DECONZ_DOMAIN -from .deconz_event import CONF_DECONZ_EVENT, DeconzEvent +from .deconz_event import ( + CONF_DECONZ_ALARM_EVENT, + CONF_DECONZ_EVENT, + DeconzAlarmEvent, + DeconzEvent, +) from .device_trigger import ( CONF_BOTH_BUTTONS, CONF_BOTTOM_BUTTONS, @@ -123,6 +128,20 @@ def async_describe_events( ) -> None: """Describe logbook events.""" + @callback + def async_describe_deconz_alarm_event(event: Event) -> dict: + """Describe deCONZ logbook alarm event.""" + deconz_alarm_event: DeconzAlarmEvent | None = _get_deconz_event_from_device_id( + hass, event.data[ATTR_DEVICE_ID] + ) + + data = event.data[CONF_EVENT] + + return { + "name": f"{deconz_alarm_event.device.name}", + "message": f"fired event '{data}'.", + } + @callback def async_describe_deconz_event(event: Event) -> dict: """Describe deCONZ logbook event.""" @@ -165,4 +184,7 @@ def async_describe_events( "message": f"'{ACTIONS[action]}' event for '{INTERFACES[interface]}' was fired.", } + async_describe_event( + DECONZ_DOMAIN, CONF_DECONZ_ALARM_EVENT, async_describe_deconz_alarm_event + ) async_describe_event(DECONZ_DOMAIN, CONF_DECONZ_EVENT, async_describe_deconz_event) diff --git a/tests/components/deconz/test_deconz_event.py b/tests/components/deconz/test_deconz_event.py index ed488e05459..4024b516498 100644 --- a/tests/components/deconz/test_deconz_event.py +++ b/tests/components/deconz/test_deconz_event.py @@ -265,7 +265,19 @@ async def test_deconz_alarm_events(hass, aioclient_mock, mock_deconz_websocket): CONF_CODE: "1234", } - # Unsupported event + # Unsupported events + + event_changed_sensor = { + "t": "event", + "e": "changed", + "r": "sensors", + "id": "1", + "state": {"action": "unsupported,1234,1"}, + } + await mock_deconz_websocket(data=event_changed_sensor) + await hass.async_block_till_done() + + assert len(captured_events) == 1 event_changed_sensor = { "t": "event", diff --git a/tests/components/deconz/test_logbook.py b/tests/components/deconz/test_logbook.py index e8e5244a222..bfd5126c9db 100644 --- a/tests/components/deconz/test_logbook.py +++ b/tests/components/deconz/test_logbook.py @@ -4,8 +4,18 @@ from unittest.mock import patch from homeassistant.components import logbook from homeassistant.components.deconz.const import CONF_GESTURE, DOMAIN as DECONZ_DOMAIN -from homeassistant.components.deconz.deconz_event import CONF_DECONZ_EVENT -from homeassistant.const import CONF_DEVICE_ID, CONF_EVENT, CONF_ID, CONF_UNIQUE_ID +from homeassistant.components.deconz.deconz_event import ( + CONF_DECONZ_ALARM_EVENT, + CONF_DECONZ_EVENT, +) +from homeassistant.const import ( + CONF_CODE, + CONF_DEVICE_ID, + CONF_EVENT, + CONF_ID, + CONF_UNIQUE_ID, + STATE_ALARM_ARMED_AWAY, +) from homeassistant.setup import async_setup_component from homeassistant.util import slugify @@ -14,6 +24,76 @@ from .test_gateway import DECONZ_WEB_REQUEST, setup_deconz_integration from tests.components.logbook.test_init import MockLazyEventPartialState +async def test_humanifying_deconz_alarm_event(hass, aioclient_mock): + """Test humanifying deCONZ event.""" + data = { + "sensors": { + "1": { + "config": { + "armed": "disarmed", + "enrolled": 0, + "on": True, + "panel": "disarmed", + "pending": [], + "reachable": True, + }, + "ep": 1, + "etag": "3c4008d74035dfaa1f0bb30d24468b12", + "lastseen": "2021-04-02T13:07Z", + "manufacturername": "Universal Electronics Inc", + "modelid": "URC4450BC0-X-R", + "name": "Keypad", + "state": { + "action": "armed_away,1111,55", + "lastupdated": "2021-04-02T13:08:18.937", + "lowbattery": False, + "tampered": True, + }, + "type": "ZHAAncillaryControl", + "uniqueid": "00:0d:6f:00:13:4f:61:39-01-0501", + } + } + } + with patch.dict(DECONZ_WEB_REQUEST, data): + await setup_deconz_integration(hass, aioclient_mock) + + device_registry = await hass.helpers.device_registry.async_get_registry() + + keypad_event_id = slugify(data["sensors"]["1"]["name"]) + keypad_serial = data["sensors"]["1"]["uniqueid"].split("-", 1)[0] + keypad_entry = device_registry.async_get_device( + identifiers={(DECONZ_DOMAIN, keypad_serial)} + ) + + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + entity_attr_cache = logbook.EntityAttributeCache(hass) + + events = list( + logbook.humanify( + hass, + [ + MockLazyEventPartialState( + CONF_DECONZ_ALARM_EVENT, + { + CONF_CODE: 1234, + CONF_DEVICE_ID: keypad_entry.id, + CONF_EVENT: STATE_ALARM_ARMED_AWAY, + CONF_ID: keypad_event_id, + CONF_UNIQUE_ID: keypad_serial, + }, + ), + ], + entity_attr_cache, + {}, + ) + ) + + assert events[0]["name"] == "Keypad" + assert events[0]["domain"] == "deconz" + assert events[0]["message"] == "fired event 'armed_away'." + + async def test_humanifying_deconz_event(hass, aioclient_mock): """Test humanifying deCONZ event.""" data = {