Skip to content

Commit

Permalink
Merge pull request #171 from carsso/main
Browse files Browse the repository at this point in the history
Adding last log field to replace ifttt events
  • Loading branch information
kvj authored Jan 7, 2024
2 parents f5e61bd + cf484e7 commit 99d1232
Show file tree
Hide file tree
Showing 2 changed files with 95 additions and 34 deletions.
97 changes: 66 additions & 31 deletions custom_components/nuki_ng/nuki.py
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,41 @@ def can_web(self):
def can_bridge(self):
return True if self.token and self.bridge else False

async def web_get_last_log(self, dev_id: str):
lock_actions_map = {
1: "unlock",
2: "lock",
3: "unlatch",
4: "lock_n_go",
5: "lock_n_go_unlatch",
}
device_actions_map = {
0: lock_actions_map,
2: {
1: "activate_rto",
2: "deactivate_rto",
3: "electric_strike_actuation",
6: "activate_continuous_mode",
7: "deactivate_continuous_mode",
},
3: lock_actions_map,
4: lock_actions_map,
}
device_actions_map[4] = device_actions_map[0]
response = await self.web_async_json(
lambda r, h: r.get(self.web_url(f"/smartlock/{dev_id}/log"), headers=h)
)
_LOGGER.debug(f"web_get_last_log ({dev_id}): {response}")
for item in response:
actions_map = device_actions_map.get(item.get("deviceType"), 0)
if item.get("action") in actions_map.keys():
return {
"name": item.get("name"),
"action": actions_map[item["action"]],
"timestamp": item["date"].replace("Z", "+00:00"),
}
return dict()

async def web_get_last_unlock_log(self, dev_id: str):
actions_map = {
1: "unlock",
Expand All @@ -202,8 +237,9 @@ async def web_get_last_unlock_log(self, dev_id: str):
response = await self.web_async_json(
lambda r, h: r.get(self.web_url(f"/smartlock/{dev_id}/log"), headers=h)
)
_LOGGER.debug(f"web_get_last_unlock_log ({dev_id}): {response}")
for item in response:
if item.get("action") in (1, 3, 5):
if item.get("action") in actions_map.keys():
# unlock, unlatch, lock'n'go with unlatch
return {
"name": item.get("name"),
Expand Down Expand Up @@ -232,19 +268,20 @@ async def web_list(self):
240: "removed",
255: "unknown",
}
lock_state_map = {
0: "uncalibrated",
1: "locked",
2: "unlocking",
3: "unlocked",
4: "locking",
5: "unlatched",
6: "unlocked (lock 'n' go)",
7: "unlatching",
254: "motor blocked",
255: "undefined",
}
device_state_map = {
0: {
0: "uncalibrated",
1: "locked",
2: "unlocking",
3: "unlocked",
4: "locking",
5: "unlatched",
6: "unlocked (lock 'n' go)",
7: "unlatching",
254: "motor blocked",
255: "undefined",
},
0: lock_state_map,
2: {
0: "untrained",
1: "online",
Expand All @@ -254,18 +291,8 @@ async def web_list(self):
253: "boot run",
255: "undefined",
},
4: {
0: "uncalibrated",
1: "locked",
2: "unlocking",
3: "unlocked",
4: "locking",
5: "unlatched",
6: "unlocked (lock 'n' go)",
7: "unlatching",
254: "motor blocked",
255: "undefined",
},
3: lock_state_map,
4: lock_state_map,
}
resp = await self.web_async_json(
lambda r, h: r.get(self.web_url(f"/smartlock"), headers=h)
Expand Down Expand Up @@ -425,14 +452,22 @@ def web_id_for_item(item):
item["webId"] = web_id
try:
item["web_auth"] = await self.api.web_list_all_auths(web_id)
except HomeAssistantError:
except HomeAssistantError as err:
_LOGGER.warning("Despite being configured, Web API request has failed")
_LOGGER.exception(f"Error while fetching auth: {err}")
item["web_auth"] = self.device_data(dev_id).get("web_auth", {})
try:
item["last_unlock_log"] = await self.api.web_get_last_unlock_log(web_id)
except HomeAssistantError as err:
_LOGGER.warning("Despite being configured, Web API request has failed")
_LOGGER.exception("Error while fetching auth:")
_LOGGER.exception(f"Error while fetching last unlock log entry: {err}")
item["last_unlock_log"] = self.device_data(dev_id).get("last_unlock_log", {})
try:
item["last_log"] = await self.api.web_get_last_unlock_log(web_id)
except HomeAssistantError:
item["last_log"] = await self.api.web_get_last_log(web_id)
except HomeAssistantError as err:
_LOGGER.warning("Despite being configured, Web API request has failed")
_LOGGER.exception("Error while fetching last log entry")
_LOGGER.exception(f"Error while fetching last log entry: {err}")
item["last_log"] = self.device_data(dev_id).get("last_log", {})
if web_list:
item["config"] = web_list.get(web_id, {}).get("config")
item["advancedConfig"] = web_list.get(web_id, {}).get("advancedConfig")
Expand Down Expand Up @@ -507,7 +542,7 @@ def info_data(self):
return self.data.get("info", {})

def is_lock(self, dev_id: str) -> bool:
return self.device_data(dev_id).get("deviceType") in (0, 4)
return self.device_data(dev_id).get("deviceType") in (0, 3, 4)

def is_opener(self, dev_id: str) -> bool:
return self.device_data(dev_id).get("deviceType") == 2
Expand Down
32 changes: 29 additions & 3 deletions custom_components/nuki_ng/sensor.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ async def async_setup_entry(hass, entry, async_add_entities):
entities.append(DoorSensorState(coordinator, dev_id))
entities.append(DoorSecurityState(coordinator, dev_id))
if coordinator.info_field(dev_id, None, "last_log"):
entities.append(LastLog(coordinator, dev_id))
if coordinator.info_field(dev_id, None, "last_unlock_log"):
entities.append(LastUnlockUser(coordinator, dev_id))

async_add_entities(entities)
Expand Down Expand Up @@ -199,6 +201,30 @@ def state(self):
def entity_category(self):
return EntityCategory.DIAGNOSTIC

class LastLog(NukiEntity, SensorEntity):

def __init__(self, coordinator, device_id):
super().__init__(coordinator, device_id)
self.set_id("sensor", "last_log")
self.set_name("Last Log")
self._attr_icon = "mdi:history"

@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 isinstance(timestamp, str) else None,
"action": action,
}

@property
def entity_category(self):
return EntityCategory.DIAGNOSTIC

class LastUnlockUser(NukiEntity, SensorEntity):

Expand All @@ -210,12 +236,12 @@ def __init__(self, coordinator, device_id):

@property
def state(self):
return self.coordinator.info_field(self.device_id, "Unknown", "last_log", "name")
return self.coordinator.info_field(self.device_id, "Unknown", "last_unlock_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")
timestamp = self.coordinator.info_field(self.device_id, None, "last_unlock_log", "timestamp")
action = self.coordinator.info_field(self.device_id, "unknown", "last_unlock_log", "action")
return {
"timestamp": datetime.fromisoformat(timestamp) if isinstance(timestamp, str) else None,
"action": action,
Expand Down

0 comments on commit 99d1232

Please sign in to comment.