diff --git a/custom_components/nuki_ng/nuki.py b/custom_components/nuki_ng/nuki.py index 933c144..2e38541 100644 --- a/custom_components/nuki_ng/nuki.py +++ b/custom_components/nuki_ng/nuki.py @@ -188,6 +188,25 @@ def can_web(self): def can_bridge(self): return True if self.token and self.bridge else False + async def web_get_last_unlock_log(self, dev_id: str): + actions_map = { + 1: "unlock", + 3: "unlatch", + 5: "lock_n_go_unlatch", + } + response = await self.web_async_json( + lambda r, h: r.get(self.web_url(f"/smartlock/{dev_id}/log"), headers=h) + ) + for item in response: + if item.get("action") in (1, 3, 5): + # unlock, unlatch, lock'n'go with unlatch + return { + "name": item.get("name"), + "action": actions_map[item["action"]], + "timestamp": item["date"].replace("Z", "+00:00"), + } + return dict() + async def web_list_all_auths(self, dev_id: str): result = {} response = await self.web_async_json( @@ -375,6 +394,10 @@ async def _update(self): item["web_auth"] = await self.api.web_list_all_auths(dev_id) except ConnectionError: _LOGGER.exception("Error while fetching auth:") + try: + item["last_log"] = await self.api.web_get_last_unlock_log(dev_id) + except ConnectionError: + _LOGGER.exception("Error while fetching last log entry") if web_list: item["config"] = web_list.get(dev_id, {}).get("config") item["advancedConfig"] = web_list.get(dev_id, {}).get("advancedConfig") diff --git a/custom_components/nuki_ng/sensor.py b/custom_components/nuki_ng/sensor.py index 09c753d..f07bcb4 100644 --- a/custom_components/nuki_ng/sensor.py +++ b/custom_components/nuki_ng/sensor.py @@ -2,6 +2,7 @@ from homeassistant.helpers.entity import EntityCategory import logging +from datetime import datetime from . import NukiEntity, NukiBridge from .constants import DOMAIN @@ -28,6 +29,9 @@ async def async_setup_entry(hass, entry, async_add_entities): if coordinator.device_supports(dev_id, "doorsensorStateName"): entities.append(DoorSensorState(coordinator, dev_id)) entities.append(DoorSecurityState(coordinator, dev_id)) + if coordinator.info_field(dev_id, None, "last_log"): + entities.append(LastUnlockUser(coordinator, dev_id)) + async_add_entities(entities) return True @@ -194,3 +198,29 @@ def state(self): @property def entity_category(self): return EntityCategory.DIAGNOSTIC + + +class LastUnlockUser(NukiEntity, SensorEntity): + + def __init__(self, coordinator, device_id): + super().__init__(coordinator, device_id) + self.set_id("sensor", "last_unlock_user") + self.set_name("Last Unlock user") + self._attr_icon = "mdi:account-lock-open" + + @property + def state(self): + return self.coordinator.info_field(self.device_id, "Unknown", "last_log", "name") + + @property + def extra_state_attributes(self): + timestamp = self.coordinator.info_field(self.device_id, None, "last_log", "timestamp") + action = self.coordinator.info_field(self.device_id, "unknown", "last_log", "action") + return { + "timestamp": datetime.fromisoformat(timestamp) if not None else None, + "action": action, + } + + @property + def entity_category(self): + return EntityCategory.DIAGNOSTIC \ No newline at end of file