Add DataUpdateCoordinator to Nanoleaf (#65950)

This commit is contained in:
Milan Meulemans 2022-02-06 23:11:52 +01:00 committed by GitHub
parent 275d4b9770
commit b1dcf7e0d8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 49 additions and 60 deletions

View File

@ -3,15 +3,17 @@ from __future__ import annotations
import asyncio import asyncio
from dataclasses import dataclass from dataclasses import dataclass
from datetime import timedelta
import logging
from aionanoleaf import EffectsEvent, InvalidToken, Nanoleaf, StateEvent, Unavailable from aionanoleaf import EffectsEvent, InvalidToken, Nanoleaf, StateEvent, Unavailable
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_HOST, CONF_TOKEN, Platform from homeassistant.const import CONF_HOST, CONF_TOKEN, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryAuthFailed, ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryAuthFailed
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from homeassistant.helpers.dispatcher import async_dispatcher_send from homeassistant.helpers.update_coordinator import DataUpdateCoordinator, UpdateFailed
from .const import DOMAIN from .const import DOMAIN
@ -23,6 +25,7 @@ class NanoleafEntryData:
"""Class for sharing data within the Nanoleaf integration.""" """Class for sharing data within the Nanoleaf integration."""
device: Nanoleaf device: Nanoleaf
coordinator: DataUpdateCoordinator
event_listener: asyncio.Task event_listener: asyncio.Task
@ -31,26 +34,39 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
nanoleaf = Nanoleaf( nanoleaf = Nanoleaf(
async_get_clientsession(hass), entry.data[CONF_HOST], entry.data[CONF_TOKEN] async_get_clientsession(hass), entry.data[CONF_HOST], entry.data[CONF_TOKEN]
) )
try:
await nanoleaf.get_info()
except Unavailable as err:
raise ConfigEntryNotReady from err
except InvalidToken as err:
raise ConfigEntryAuthFailed from err
async def _callback_update_light_state(event: StateEvent | EffectsEvent) -> None: async def async_get_state() -> None:
"""Get the state of the device."""
try:
await nanoleaf.get_info()
except Unavailable as err:
raise UpdateFailed from err
except InvalidToken as err:
raise ConfigEntryAuthFailed from err
coordinator = DataUpdateCoordinator(
hass,
logging.getLogger(__name__),
name=entry.title,
update_interval=timedelta(minutes=1),
update_method=async_get_state,
)
await coordinator.async_config_entry_first_refresh()
async def update_light_state_callback(event: StateEvent | EffectsEvent) -> None:
"""Receive state and effect event.""" """Receive state and effect event."""
async_dispatcher_send(hass, f"{DOMAIN}_update_light_{nanoleaf.serial_no}") coordinator.async_set_updated_data(None)
event_listener = asyncio.create_task( event_listener = asyncio.create_task(
nanoleaf.listen_events( nanoleaf.listen_events(
state_callback=_callback_update_light_state, state_callback=update_light_state_callback,
effects_callback=_callback_update_light_state, effects_callback=update_light_state_callback,
) )
) )
hass.data.setdefault(DOMAIN, {})[entry.entry_id] = NanoleafEntryData( hass.data.setdefault(DOMAIN, {})[entry.entry_id] = NanoleafEntryData(
nanoleaf, event_listener nanoleaf, coordinator, event_listener
) )
hass.config_entries.async_setup_platforms(entry, PLATFORMS) hass.config_entries.async_setup_platforms(entry, PLATFORMS)

View File

@ -7,6 +7,7 @@ from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity import EntityCategory from homeassistant.helpers.entity import EntityCategory
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from . import NanoleafEntryData from . import NanoleafEntryData
from .const import DOMAIN from .const import DOMAIN
@ -18,15 +19,17 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up the Nanoleaf button.""" """Set up the Nanoleaf button."""
entry_data: NanoleafEntryData = hass.data[DOMAIN][entry.entry_id] entry_data: NanoleafEntryData = hass.data[DOMAIN][entry.entry_id]
async_add_entities([NanoleafIdentifyButton(entry_data.device)]) async_add_entities(
[NanoleafIdentifyButton(entry_data.device, entry_data.coordinator)]
)
class NanoleafIdentifyButton(NanoleafEntity, ButtonEntity): class NanoleafIdentifyButton(NanoleafEntity, ButtonEntity):
"""Representation of a Nanoleaf identify button.""" """Representation of a Nanoleaf identify button."""
def __init__(self, nanoleaf: Nanoleaf) -> None: def __init__(self, nanoleaf: Nanoleaf, coordinator: DataUpdateCoordinator) -> None:
"""Initialize the Nanoleaf button.""" """Initialize the Nanoleaf button."""
super().__init__(nanoleaf) super().__init__(nanoleaf, coordinator)
self._attr_unique_id = f"{nanoleaf.serial_no}_identify" self._attr_unique_id = f"{nanoleaf.serial_no}_identify"
self._attr_name = f"Identify {nanoleaf.name}" self._attr_name = f"Identify {nanoleaf.name}"
self._attr_icon = "mdi:magnify" self._attr_icon = "mdi:magnify"

View File

@ -2,16 +2,21 @@
from aionanoleaf import Nanoleaf from aionanoleaf import Nanoleaf
from homeassistant.helpers.entity import DeviceInfo, Entity from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.update_coordinator import (
CoordinatorEntity,
DataUpdateCoordinator,
)
from .const import DOMAIN from .const import DOMAIN
class NanoleafEntity(Entity): class NanoleafEntity(CoordinatorEntity):
"""Representation of a Nanoleaf entity.""" """Representation of a Nanoleaf entity."""
def __init__(self, nanoleaf: Nanoleaf) -> None: def __init__(self, nanoleaf: Nanoleaf, coordinator: DataUpdateCoordinator) -> None:
"""Initialize an Nanoleaf entity.""" """Initialize an Nanoleaf entity."""
super().__init__(coordinator)
self._nanoleaf = nanoleaf self._nanoleaf = nanoleaf
self._attr_device_info = DeviceInfo( self._attr_device_info = DeviceInfo(
identifiers={(DOMAIN, nanoleaf.serial_no)}, identifiers={(DOMAIN, nanoleaf.serial_no)},

View File

@ -1,12 +1,11 @@
"""Support for Nanoleaf Lights.""" """Support for Nanoleaf Lights."""
from __future__ import annotations from __future__ import annotations
from datetime import timedelta
import logging import logging
import math import math
from typing import Any from typing import Any
from aionanoleaf import Nanoleaf, Unavailable from aionanoleaf import Nanoleaf
import voluptuous as vol import voluptuous as vol
from homeassistant.components.light import ( from homeassistant.components.light import (
@ -25,11 +24,11 @@ from homeassistant.components.light import (
) )
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN from homeassistant.const import CONF_HOST, CONF_NAME, CONF_TOKEN
from homeassistant.core import HomeAssistant, callback from homeassistant.core import HomeAssistant
import homeassistant.helpers.config_validation as cv import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.dispatcher import async_dispatcher_connect
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
from homeassistant.util.color import ( from homeassistant.util.color import (
color_temperature_kelvin_to_mired as kelvin_to_mired, color_temperature_kelvin_to_mired as kelvin_to_mired,
color_temperature_mired_to_kelvin as mired_to_kelvin, color_temperature_mired_to_kelvin as mired_to_kelvin,
@ -52,8 +51,6 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend(
_LOGGER = logging.getLogger(__name__) _LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(minutes=5)
async def async_setup_platform( async def async_setup_platform(
hass: HomeAssistant, hass: HomeAssistant,
@ -82,15 +79,15 @@ async def async_setup_entry(
) -> None: ) -> None:
"""Set up the Nanoleaf light.""" """Set up the Nanoleaf light."""
entry_data: NanoleafEntryData = hass.data[DOMAIN][entry.entry_id] entry_data: NanoleafEntryData = hass.data[DOMAIN][entry.entry_id]
async_add_entities([NanoleafLight(entry_data.device)]) async_add_entities([NanoleafLight(entry_data.device, entry_data.coordinator)])
class NanoleafLight(NanoleafEntity, LightEntity): class NanoleafLight(NanoleafEntity, LightEntity):
"""Representation of a Nanoleaf Light.""" """Representation of a Nanoleaf Light."""
def __init__(self, nanoleaf: Nanoleaf) -> None: def __init__(self, nanoleaf: Nanoleaf, coordinator: DataUpdateCoordinator) -> None:
"""Initialize the Nanoleaf light.""" """Initialize the Nanoleaf light."""
super().__init__(nanoleaf) super().__init__(nanoleaf, coordinator)
self._attr_unique_id = nanoleaf.serial_no self._attr_unique_id = nanoleaf.serial_no
self._attr_name = nanoleaf.name self._attr_name = nanoleaf.name
self._attr_min_mireds = math.ceil(1000000 / nanoleaf.color_temperature_max) self._attr_min_mireds = math.ceil(1000000 / nanoleaf.color_temperature_max)
@ -186,35 +183,3 @@ class NanoleafLight(NanoleafEntity, LightEntity):
"""Instruct the light to turn off.""" """Instruct the light to turn off."""
transition: float | None = kwargs.get(ATTR_TRANSITION) transition: float | None = kwargs.get(ATTR_TRANSITION)
await self._nanoleaf.turn_off(None if transition is None else int(transition)) await self._nanoleaf.turn_off(None if transition is None else int(transition))
async def async_update(self) -> None:
"""Fetch new state data for this light."""
try:
await self._nanoleaf.get_info()
except Unavailable:
if self.available:
_LOGGER.warning("Could not connect to %s", self.name)
self._attr_available = False
return
if not self.available:
_LOGGER.info("Fetching %s data recovered", self.name)
self._attr_available = True
@callback
def async_handle_update(self) -> None:
"""Handle state update."""
self.async_write_ha_state()
if not self.available:
_LOGGER.info("Connection to %s recovered", self.name)
self._attr_available = True
async def async_added_to_hass(self) -> None:
"""Handle entity being added to Home Assistant."""
await super().async_added_to_hass()
self.async_on_remove(
async_dispatcher_connect(
self.hass,
f"{DOMAIN}_update_light_{self._nanoleaf.serial_no}",
self.async_handle_update,
)
)