diff --git a/homeassistant/components/mobile_app/logbook.py b/homeassistant/components/mobile_app/logbook.py new file mode 100644 index 00000000000..6dd4d007e3e --- /dev/null +++ b/homeassistant/components/mobile_app/logbook.py @@ -0,0 +1,54 @@ +"""Describe mobile_app logbook events.""" +from __future__ import annotations + +from collections.abc import Callable + +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON +from homeassistant.core import Event, HomeAssistant, callback + +from .const import DOMAIN + +IOS_EVENT_ZONE_ENTERED = "ios.zone_entered" +IOS_EVENT_ZONE_EXITED = "ios.zone_exited" + +ATTR_ZONE = "zone" +ATTR_SOURCE_DEVICE_NAME = "sourceDeviceName" +ATTR_SOURCE_DEVICE_ID = "sourceDeviceID" +EVENT_TO_DESCRIPTION = { + IOS_EVENT_ZONE_ENTERED: "entered zone", + IOS_EVENT_ZONE_EXITED: "exited zone", +} + + +@callback +def async_describe_events( + hass: HomeAssistant, + async_describe_event: Callable[[str, str, Callable[[Event], dict[str, str]]], None], +) -> None: + """Describe logbook events.""" + + @callback + def async_describe_zone_event(event: Event) -> dict[str, str]: + """Describe mobile_app logbook event.""" + data = event.data + event_description = EVENT_TO_DESCRIPTION[event.event_type] + zone_entity_id = data.get(ATTR_ZONE) + source_device_name = data.get( + ATTR_SOURCE_DEVICE_NAME, data.get(ATTR_SOURCE_DEVICE_ID) + ) + zone_name = None + zone_icon = None + if zone_entity_id and (zone_state := hass.states.get(zone_entity_id)): + zone_name = zone_state.attributes.get(ATTR_FRIENDLY_NAME) + zone_icon = zone_state.attributes.get(ATTR_ICON) + description = { + "name": source_device_name, + "message": f"{event_description} {zone_name or zone_entity_id}", + "icon": zone_icon or "mdi:crosshairs-gps", + } + if zone_entity_id: + description["entity_id"] = zone_entity_id + return description + + async_describe_event(DOMAIN, IOS_EVENT_ZONE_ENTERED, async_describe_zone_event) + async_describe_event(DOMAIN, IOS_EVENT_ZONE_EXITED, async_describe_zone_event) diff --git a/tests/components/mobile_app/test_logbook.py b/tests/components/mobile_app/test_logbook.py new file mode 100644 index 00000000000..b151bd11a26 --- /dev/null +++ b/tests/components/mobile_app/test_logbook.py @@ -0,0 +1,50 @@ +"""The tests for mobile_app logbook.""" + +from homeassistant.components.mobile_app.logbook import ( + DOMAIN, + IOS_EVENT_ZONE_ENTERED, + IOS_EVENT_ZONE_EXITED, +) +from homeassistant.const import ATTR_FRIENDLY_NAME, ATTR_ICON +from homeassistant.setup import async_setup_component + +from tests.components.logbook.common import MockRow, mock_humanify + + +async def test_humanify_ios_events(hass): + """Test humanifying ios events.""" + hass.config.components.add("recorder") + assert await async_setup_component(hass, "logbook", {}) + await async_setup_component(hass, DOMAIN, {DOMAIN: {}}) + hass.states.async_set( + "zone.bad_place", + "0", + {ATTR_FRIENDLY_NAME: "passport control", ATTR_ICON: "mdi:airplane-marker"}, + ) + await hass.async_block_till_done() + + (event1, event2) = mock_humanify( + hass, + [ + MockRow( + IOS_EVENT_ZONE_ENTERED, + {"sourceDeviceName": "test_phone", "zone": "zone.happy_place"}, + ), + MockRow( + IOS_EVENT_ZONE_EXITED, + {"sourceDeviceName": "test_phone", "zone": "zone.bad_place"}, + ), + ], + ) + + assert event1["name"] == "test_phone" + assert event1["domain"] == DOMAIN + assert event1["message"] == "entered zone zone.happy_place" + assert event1["icon"] == "mdi:crosshairs-gps" + assert event1["entity_id"] == "zone.happy_place" + + assert event2["name"] == "test_phone" + assert event2["domain"] == DOMAIN + assert event2["message"] == "exited zone passport control" + assert event2["icon"] == "mdi:airplane-marker" + assert event2["entity_id"] == "zone.bad_place"