Nuki opener event on ring (#72793)

* feat(nuki): add ring action timestamp attribute

* feat(nuki): add ring action state attribute

* Emit event on Nuki Opener ring

* Removed event attributes

* Use entity registry to get entity id

* Move event firing to the async update method

* Move events code outside try-except

* Black autoformat

* Added missing period to doc

* Import order

Co-authored-by: Franck Nijhof <git@frenck.dev>
pull/74084/head
Iván Cea Fontenla 2022-06-28 01:47:55 +02:00 committed by GitHub
parent 09dca3cd94
commit c62bfcaa4c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 38 additions and 3 deletions

View File

@ -1,4 +1,5 @@
"""The nuki component."""
from collections import defaultdict
from datetime import timedelta
import logging
@ -12,6 +13,7 @@ from homeassistant import exceptions
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_PORT, CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
@ -39,17 +41,36 @@ def _get_bridge_devices(bridge: NukiBridge) -> tuple[list[NukiLock], list[NukiOp
return bridge.locks, bridge.openers
def _update_devices(devices: list[NukiDevice]) -> None:
def _update_devices(devices: list[NukiDevice]) -> dict[str, set[str]]:
"""
Update the Nuki devices.
Returns:
A dict with the events to be fired. The event type is the key and the device ids are the value
"""
events: dict[str, set[str]] = defaultdict(set)
for device in devices:
for level in (False, True):
try:
device.update(level)
if isinstance(device, NukiOpener):
last_ring_action_state = device.ring_action_state
device.update(level)
if not last_ring_action_state and device.ring_action_state:
events["ring"].add(device.nuki_id)
else:
device.update(level)
except RequestException:
continue
if device.state not in ERROR_STATES:
break
return events
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up the Nuki entry."""
@ -86,12 +107,26 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
# Note: asyncio.TimeoutError and aiohttp.ClientError are already
# handled by the data update coordinator.
async with async_timeout.timeout(10):
await hass.async_add_executor_job(_update_devices, locks + openers)
events = await hass.async_add_executor_job(
_update_devices, locks + openers
)
except InvalidCredentialsException as err:
raise UpdateFailed(f"Invalid credentials for Bridge: {err}") from err
except RequestException as err:
raise UpdateFailed(f"Error communicating with Bridge: {err}") from err
ent_reg = er.async_get(hass)
for event, device_ids in events.items():
for device_id in device_ids:
entity_id = ent_reg.async_get_entity_id(
Platform.LOCK, DOMAIN, device_id
)
event_data = {
"entity_id": entity_id,
"type": event,
}
hass.bus.async_fire("nuki_event", event_data)
coordinator = DataUpdateCoordinator(
hass,
_LOGGER,