From 15281f468e3abd04520f5017134de7191016f00c Mon Sep 17 00:00:00 2001 From: "J. Nick Koston" Date: Mon, 14 Sep 2020 09:35:19 -0500 Subject: [PATCH] Extract the icon and state for logbook state changed events (#40039) --- homeassistant/components/logbook/__init__.py | 16 ++++++ tests/components/logbook/test_init.py | 52 ++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/homeassistant/components/logbook/__init__.py b/homeassistant/components/logbook/__init__.py index 0c7786de90b..476aa62501a 100644 --- a/homeassistant/components/logbook/__init__.py +++ b/homeassistant/components/logbook/__init__.py @@ -26,6 +26,7 @@ from homeassistant.const import ( ATTR_DOMAIN, ATTR_ENTITY_ID, ATTR_FRIENDLY_NAME, + ATTR_ICON, ATTR_NAME, ATTR_SERVICE, EVENT_CALL_SERVICE, @@ -60,6 +61,7 @@ import homeassistant.util.dt as dt_util ENTITY_ID_JSON_TEMPLATE = '"entity_id": "{}"' ENTITY_ID_JSON_EXTRACT = re.compile('"entity_id": "([^"]+)"') DOMAIN_JSON_EXTRACT = re.compile('"domain": "([^"]+)"') +ICON_JSON_EXTRACT = re.compile('"icon": "([^"]+)"') _LOGGER = logging.getLogger(__name__) @@ -321,9 +323,14 @@ def humanify(hass, events, entity_attr_cache, context_lookup): entity_id, domain, event, entity_attr_cache ), "domain": domain, + "state": event.state, "entity_id": entity_id, } + icon = event.attributes_icon + if icon: + data["icon"] = icon + if event.context_user_id: data["context_user_id"] = event.context_user_id @@ -724,6 +731,15 @@ class LazyEventPartialState: self.context_user_id = self._row.context_user_id self.time_fired_minute = self._row.time_fired.minute + @property + def attributes_icon(self): + """Extract the icon from the decoded attributes or json.""" + if self._attributes: + return self._attributes.get(ATTR_ICON) + + result = ICON_JSON_EXTRACT.search(self._row.attributes) + return result and result.group(1) + @property def data_entity_id(self): """Extract the entity id from the decoded data or json.""" diff --git a/tests/components/logbook/test_init.py b/tests/components/logbook/test_init.py index 5e41f0bce89..745d809ca7f 100644 --- a/tests/components/logbook/test_init.py +++ b/tests/components/logbook/test_init.py @@ -2176,6 +2176,58 @@ async def test_logbook_invalid_entity(hass, hass_client): assert response.status == 500 +async def test_icon_and_state(hass, hass_client): + """Test to ensure state and custom icons are returned.""" + await hass.async_add_executor_job(init_recorder_component, hass) + await async_setup_component(hass, "logbook", {}) + await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done) + + hass.bus.async_fire(EVENT_HOMEASSISTANT_START) + + hass.states.async_set("light.kitchen", STATE_OFF, {"icon": "mdi:chemical-weapon"}) + hass.states.async_set( + "light.kitchen", STATE_ON, {"brightness": 100, "icon": "mdi:security"} + ) + hass.states.async_set( + "light.kitchen", STATE_ON, {"brightness": 200, "icon": "mdi:security"} + ) + hass.states.async_set( + "light.kitchen", STATE_ON, {"brightness": 300, "icon": "mdi:security"} + ) + hass.states.async_set( + "light.kitchen", STATE_ON, {"brightness": 400, "icon": "mdi:security"} + ) + hass.states.async_set("light.kitchen", STATE_OFF, {"icon": "mdi:chemical-weapon"}) + + await hass.async_block_till_done() + + await hass.async_add_job(trigger_db_commit, hass) + await hass.async_block_till_done() + await hass.async_add_job(hass.data[recorder.DATA_INSTANCE].block_till_done) + + client = await hass_client() + + # Today time 00:00:00 + start = dt_util.utcnow().date() + start_date = datetime(start.year, start.month, start.day) + + # Test today entries without filters + response = await client.get(f"/api/logbook/{start_date.isoformat()}") + assert response.status == 200 + response_json = await response.json() + + assert len(response_json) == 3 + assert response_json[0]["domain"] == "homeassistant" + assert response_json[1]["message"] == "turned on" + assert response_json[1]["entity_id"] == "light.kitchen" + assert response_json[1]["icon"] == "mdi:security" + assert response_json[1]["state"] == STATE_ON + assert response_json[2]["message"] == "turned off" + assert response_json[2]["entity_id"] == "light.kitchen" + assert response_json[2]["icon"] == "mdi:chemical-weapon" + assert response_json[2]["state"] == STATE_OFF + + class MockLazyEventPartialState(ha.Event): """Minimal mock of a Lazy event."""