Add coordinator to Knocki (#120251)

pull/120416/head
Joost Lekkerkerker 2024-06-24 11:41:33 +02:00 committed by GitHub
parent c04a6cc639
commit f3a1ca6d54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 89 additions and 22 deletions

View File

@ -2,27 +2,18 @@
from __future__ import annotations
from dataclasses import dataclass
from knocki import KnockiClient, KnockiConnectionError, Trigger
from knocki import EventType, KnockiClient
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .coordinator import KnockiCoordinator
PLATFORMS: list[Platform] = [Platform.EVENT]
type KnockiConfigEntry = ConfigEntry[KnockiData]
@dataclass
class KnockiData:
"""Knocki data."""
client: KnockiClient
triggers: list[Trigger]
type KnockiConfigEntry = ConfigEntry[KnockiCoordinator]
async def async_setup_entry(hass: HomeAssistant, entry: KnockiConfigEntry) -> bool:
@ -31,12 +22,15 @@ async def async_setup_entry(hass: HomeAssistant, entry: KnockiConfigEntry) -> bo
session=async_get_clientsession(hass), token=entry.data[CONF_TOKEN]
)
try:
triggers = await client.get_triggers()
except KnockiConnectionError as exc:
raise ConfigEntryNotReady from exc
coordinator = KnockiCoordinator(hass, client)
entry.runtime_data = KnockiData(client=client, triggers=triggers)
await coordinator.async_config_entry_first_refresh()
entry.async_on_unload(
client.register_listener(EventType.CREATED, coordinator.add_trigger)
)
entry.runtime_data = coordinator
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)

View File

@ -0,0 +1,34 @@
"""Update coordinator for Knocki integration."""
from knocki import Event, KnockiClient, KnockiConnectionError, Trigger
from homeassistant.core import HomeAssistant
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN, LOGGER
class KnockiCoordinator(DataUpdateCoordinator[dict[int, Trigger]]):
"""The Knocki coordinator."""
def __init__(self, hass: HomeAssistant, client: KnockiClient) -> None:
"""Initialize the coordinator."""
super().__init__(
hass,
logger=LOGGER,
name=DOMAIN,
)
self.client = client
async def _async_update_data(self) -> dict[int, Trigger]:
try:
triggers = await self.client.get_triggers()
except KnockiConnectionError as exc:
raise UpdateFailed from exc
return {trigger.details.trigger_id: trigger for trigger in triggers}
def add_trigger(self, event: Event) -> None:
"""Add a trigger to the coordinator."""
self.async_set_updated_data(
{**self.data, event.payload.details.trigger_id: event.payload}
)

View File

@ -3,7 +3,7 @@
from knocki import Event, EventType, KnockiClient, Trigger
from homeassistant.components.event import EventEntity
from homeassistant.core import HomeAssistant
from homeassistant.core import HomeAssistant, callback
from homeassistant.helpers.device_registry import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
@ -17,10 +17,26 @@ async def async_setup_entry(
async_add_entities: AddEntitiesCallback,
) -> None:
"""Set up Knocki from a config entry."""
entry_data = entry.runtime_data
coordinator = entry.runtime_data
added_triggers = set(coordinator.data)
@callback
def _async_add_entities() -> None:
current_triggers = set(coordinator.data)
new_triggers = current_triggers - added_triggers
added_triggers.update(new_triggers)
if new_triggers:
async_add_entities(
KnockiTrigger(coordinator.data[trigger], coordinator.client)
for trigger in new_triggers
)
coordinator.async_add_listener(_async_add_entities)
async_add_entities(
KnockiTrigger(trigger, entry_data.client) for trigger in entry_data.triggers
KnockiTrigger(trigger, coordinator.client)
for trigger in coordinator.data.values()
)

View File

@ -7,13 +7,14 @@ from knocki import Event, EventType, Trigger, TriggerDetails
import pytest
from syrupy import SnapshotAssertion
from homeassistant.components.knocki.const import DOMAIN
from homeassistant.const import STATE_UNKNOWN
from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er
from . import setup_integration
from tests.common import MockConfigEntry, snapshot_platform
from tests.common import MockConfigEntry, load_json_array_fixture, snapshot_platform
async def test_entities(
@ -73,3 +74,25 @@ async def test_subscription(
await hass.async_block_till_done()
assert mock_knocki_client.register_listener.return_value.called
async def test_adding_runtime_entities(
hass: HomeAssistant,
mock_knocki_client: AsyncMock,
mock_config_entry: MockConfigEntry,
) -> None:
"""Test we can create devices on runtime."""
mock_knocki_client.get_triggers.return_value = []
await setup_integration(hass, mock_config_entry)
assert not hass.states.get("event.knc1_w_00000214_aaaa")
add_trigger_function: Callable[[Event], None] = (
mock_knocki_client.register_listener.call_args[0][1]
)
trigger = Trigger.from_dict(load_json_array_fixture("triggers.json", DOMAIN)[0])
add_trigger_function(Event(EventType.CREATED, trigger))
assert hass.states.get("event.knc1_w_00000214_aaaa") is not None