mirror of
https://github.com/home-assistant/core.git
synced 2025-07-25 06:07:17 +00:00
Add DataUpdateCoordinator to Nanoleaf (#65950)
This commit is contained in:
parent
275d4b9770
commit
b1dcf7e0d8
@ -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)
|
||||||
|
@ -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"
|
||||||
|
@ -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)},
|
||||||
|
@ -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,
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user