Skip to content

Commit 0b3fb37

Browse files
authored
Merge pull request #100 from imhotep/thumbnail-error
error handling for thumbnails
2 parents 2b89cd5 + c023a4c commit 0b3fb37

File tree

7 files changed

+97
-36
lines changed

7 files changed

+97
-36
lines changed

custom_components/unifi_access/__init__.py

+21
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@
22

33
from __future__ import annotations
44

5+
import logging
6+
57
from homeassistant.config_entries import ConfigEntry
68
from homeassistant.const import Platform
79
from homeassistant.core import HomeAssistant
10+
import homeassistant.helpers.entity_registry as er
811

912
from .const import DOMAIN
1013
from .coordinator import UnifiAccessCoordinator
@@ -20,6 +23,22 @@
2023
Platform.SENSOR,
2124
Platform.SWITCH,
2225
]
26+
_LOGGER = logging.getLogger(__name__)
27+
28+
29+
async def remove_stale_entities(hass: HomeAssistant, entry_id: str):
30+
"""Remove entities that are stale."""
31+
_LOGGER.debug("Removing stale entities")
32+
registry = er.async_get(hass)
33+
config_entry_entities = registry.entities.get_entries_for_config_entry_id(entry_id)
34+
stale_entities = [
35+
entity
36+
for entity in config_entry_entities
37+
if (entity.disabled or not hass.states.get(entity.entity_id))
38+
]
39+
for entity in stale_entities:
40+
_LOGGER.debug("Removing stale entity: %s", entity.entity_id)
41+
registry.async_remove(entity.entity_id)
2342

2443

2544
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
@@ -38,6 +57,8 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
3857

3958
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
4059

60+
await remove_stale_entities(hass, entry.entry_id)
61+
4162
return True
4263

4364

custom_components/unifi_access/coordinator.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def __init__(self, hass: HomeAssistant, hub) -> None:
2727
hass,
2828
_LOGGER,
2929
name="Unifi Access Coordinator",
30-
always_update=False,
30+
always_update=True,
3131
update_interval=update_interval,
3232
)
3333
self.hub = hub

custom_components/unifi_access/door.py

+18
Original file line numberDiff line numberDiff line change
@@ -147,3 +147,21 @@ async def trigger_event(self, event: str, data: dict[str, str]):
147147
_LOGGER.info(
148148
"Event %s type %s for door %s fired", event, data["type"], self.name
149149
)
150+
151+
def __eq__(self, value) -> bool:
152+
"""Check if two doors are equal."""
153+
if isinstance(value, UnifiAccessDoor):
154+
return (
155+
self.id == value.id
156+
and self.name == value.name
157+
and self.hub_type == value.hub_type
158+
and self.door_position_status == value.door_position_status
159+
and self.door_lock_relay_status == value.door_lock_relay_status
160+
and self.lock_rule == value.lock_rule
161+
and self.lock_rule_ended_time == value.lock_rule_ended_time
162+
)
163+
return False
164+
165+
def __repr__(self):
166+
"""Return string representation of door."""
167+
return f"<UnifiAccessDoor id={self.id} name={self.name} hub_type={self.hub_type} door_position_status={self.door_position_status} door_lock_relay_status={self.door_lock_relay_status} lock_rule={self.lock_rule} lock_rule_ended_time={self.lock_rule_ended_time}>"

custom_components/unifi_access/event.py

+13-7
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
DOORBELL_STOP_EVENT,
1919
)
2020
from .door import UnifiAccessDoor
21+
from .hub import UnifiAccessHub
2122

2223
_LOGGER = logging.getLogger(__name__)
2324

@@ -28,15 +29,20 @@ async def async_setup_entry(
2829
async_add_entities: AddEntitiesCallback,
2930
) -> None:
3031
"""Add event entity for passed config entry."""
32+
hub: UnifiAccessHub = hass.data[DOMAIN][config_entry.entry_id]
3133

32-
coordinator = hass.data[DOMAIN]["coordinator"]
34+
if hub.use_polling is False:
35+
coordinator = hass.data[DOMAIN]["coordinator"]
3336

34-
async_add_entities(
35-
(AccessEventEntity(hass, door) for door in coordinator.data.values()),
36-
)
37-
async_add_entities(
38-
(DoorbellPressedEventEntity(hass, door) for door in coordinator.data.values()),
39-
)
37+
async_add_entities(
38+
(AccessEventEntity(hass, door) for door in coordinator.data.values()),
39+
)
40+
async_add_entities(
41+
(
42+
DoorbellPressedEventEntity(hass, door)
43+
for door in coordinator.data.values()
44+
),
45+
)
4046

4147

4248
class AccessEventEntity(EventEntity):

custom_components/unifi_access/hub.py

+36-23
Original file line numberDiff line numberDiff line change
@@ -119,12 +119,12 @@ def update(self):
119119
"Getting door updates from Unifi Access %s Use Polling %s. Doors? %s",
120120
self.host,
121121
self.use_polling,
122-
self.doors,
122+
self.doors.keys(),
123123
)
124124
data = self._make_http_request(f"{self.host}{DOORS_URL}")
125125

126126
for _i, door in enumerate(data):
127-
if door["is_bind_hub"]:
127+
if door["is_bind_hub"] is True:
128128
door_id = door["id"]
129129
door_lock_rule = {"type": "", "ended_time": 0}
130130
if self.supports_door_lock_rules:
@@ -138,6 +138,16 @@ def update(self):
138138
]
139139
existing_door.door_lock_rule = door_lock_rule["type"]
140140
existing_door.door_lock_ended_time = door_lock_rule["ended_time"]
141+
_LOGGER.debug(
142+
"Updated existing door, id: %s, name: %s, dps: %s, door_lock_relay_status: %s, door lock rule: %s, door lock rule ended time: %s using polling %s",
143+
door_id,
144+
door["name"],
145+
door["door_position_status"],
146+
door["door_lock_relay_status"],
147+
door_lock_rule["type"],
148+
door_lock_rule["ended_time"],
149+
self.use_polling,
150+
)
141151
else:
142152
self._doors[door_id] = UnifiAccessDoor(
143153
door_id=door["id"],
@@ -148,26 +158,26 @@ def update(self):
148158
door_lock_rule_ended_time=door_lock_rule["ended_time"],
149159
hub=self,
150160
)
161+
_LOGGER.debug(
162+
"Found new door, id: %s, name: %s, dps: %s, door_lock_relay_status: %s, door lock rule: %s, door lock rule ended time: %s, using polling: %s",
163+
door_id,
164+
door["name"],
165+
door["door_position_status"],
166+
door["door_lock_relay_status"],
167+
door_lock_rule["type"],
168+
door_lock_rule["ended_time"],
169+
self.use_polling,
170+
)
171+
else:
172+
_LOGGER.debug("Door %s is not bound to a hub. Ignoring", door)
173+
151174
if self.update_t is None and self.use_polling is False:
175+
_LOGGER.debug("Starting continuous updates. Polling disabled")
152176
self.start_continuous_updates()
153177

178+
_LOGGER.debug("Got doors %s", self.doors)
154179
return self._doors
155180

156-
def update_door(self, door_id: int) -> None:
157-
"""Get latest door data for a specific door."""
158-
_LOGGER.info("Getting door update from Unifi Access with id %s", door_id)
159-
updated_door = self._make_http_request(f"{self.host}{DOORS_URL}/{door_id}")
160-
door_id = updated_door["id"]
161-
_LOGGER.debug("got door update %s", updated_door)
162-
if door_id in self.doors:
163-
existing_door: UnifiAccessDoor = self.doors[door_id]
164-
existing_door.door_lock_relay_status = updated_door[
165-
"door_lock_relay_status"
166-
]
167-
existing_door.door_position_status = updated_door["door_position_status"]
168-
existing_door.name = updated_door["name"]
169-
_LOGGER.debug("door %s updated", door_id)
170-
171181
def authenticate(self, api_token: str) -> str:
172182
"""Test if we can authenticate with the host."""
173183
self.set_api_token(api_token)
@@ -329,12 +339,15 @@ def _handle_location_update_v2(self, update):
329339
lock_rule
330340
]["until"]
331341
if "thumbnail" in update["data"]:
332-
existing_door.thumbnail = self._get_thumbnail_image(
333-
f"{self.host}{STATIC_URL}{update['data']['thumbnail']['url']}"
334-
)
335-
existing_door.thumbnail_last_updated = datetime.fromtimestamp(
336-
update["data"]["thumbnail"]["door_thumbnail_last_update"]
337-
)
342+
try:
343+
existing_door.thumbnail = self._get_thumbnail_image(
344+
f"{self.host}{STATIC_URL}{update['data']['thumbnail']['url']}"
345+
)
346+
existing_door.thumbnail_last_updated = datetime.fromtimestamp(
347+
update["data"]["thumbnail"]["door_thumbnail_last_update"]
348+
)
349+
except (ApiError, ApiAuthError):
350+
_LOGGER.error("Could not get thumbnail for door id %s", door_id)
338351
return existing_door
339352

340353
def on_message(self, ws: websocket.WebSocketApp, message):

custom_components/unifi_access/image.py

+7-4
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313

1414
from .const import DOMAIN
1515
from .door import UnifiAccessDoor
16+
from .hub import UnifiAccessHub
1617

1718
_LOGGER = logging.getLogger(__name__)
1819

@@ -26,10 +27,12 @@ async def async_setup_entry(
2627

2728
coordinator = hass.data[DOMAIN]["coordinator"]
2829
verify_ssl = config_entry.options.get("verify_ssl", False)
29-
async_add_entities(
30-
UnifiDoorImageEntity(hass, verify_ssl, door, config_entry.data["api_token"])
31-
for door in coordinator.data.values()
32-
)
30+
hub: UnifiAccessHub = hass.data[DOMAIN][config_entry.entry_id]
31+
if hub.use_polling is False:
32+
async_add_entities(
33+
UnifiDoorImageEntity(hass, verify_ssl, door, config_entry.data["api_token"])
34+
for door in coordinator.data.values()
35+
)
3336

3437

3538
class UnifiDoorImageEntity(ImageEntity):

custom_components/unifi_access/select.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ def _update_options(self):
8282
):
8383
self._attr_options.remove("lock_early")
8484
else:
85-
self._attr_options.add("lock_early")
85+
self._attr_options.append("lock_early")
8686

8787
async def async_select_option(self, option: str) -> None:
8888
"Select Door Lock Rule."

0 commit comments

Comments
 (0)