Store Twinkly runtime data in config entry (#133714)

This commit is contained in:
Maciej Bieniek 2024-12-21 11:31:40 +00:00 committed by GitHub
parent 6314d7a44c
commit 5665abf991
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
4 changed files with 41 additions and 54 deletions

View File

@ -1,29 +1,40 @@
"""The twinkly component.""" """The twinkly component."""
from dataclasses import dataclass
from typing import Any
from aiohttp import ClientError from aiohttp import ClientError
from ttls.client import Twinkly from ttls.client import Twinkly
from homeassistant.config_entries import ConfigEntry from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SW_VERSION, CONF_HOST, Platform from homeassistant.const import CONF_HOST, Platform
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers.aiohttp_client import async_get_clientsession from homeassistant.helpers.aiohttp_client import async_get_clientsession
from .const import ATTR_VERSION, DATA_CLIENT, DATA_DEVICE_INFO, DOMAIN from .const import ATTR_VERSION
PLATFORMS = [Platform.LIGHT] PLATFORMS = [Platform.LIGHT]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: @dataclass
"""Set up entries from config flow.""" class TwinklyData:
hass.data.setdefault(DOMAIN, {}) """Data for Twinkly integration."""
client: Twinkly
device_info: dict[str, Any]
sw_version: str | None
type TwinklyConfigEntry = ConfigEntry[TwinklyData]
async def async_setup_entry(hass: HomeAssistant, entry: TwinklyConfigEntry) -> bool:
"""Set up entries from config flow."""
# We setup the client here so if at some point we add any other entity for this device, # We setup the client here so if at some point we add any other entity for this device,
# we will be able to properly share the connection. # we will be able to properly share the connection.
host = entry.data[CONF_HOST] host = entry.data[CONF_HOST]
hass.data[DOMAIN].setdefault(entry.entry_id, {})
client = Twinkly(host, async_get_clientsession(hass)) client = Twinkly(host, async_get_clientsession(hass))
try: try:
@ -32,21 +43,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
except (TimeoutError, ClientError) as exception: except (TimeoutError, ClientError) as exception:
raise ConfigEntryNotReady from exception raise ConfigEntryNotReady from exception
hass.data[DOMAIN][entry.entry_id] = { entry.runtime_data = TwinklyData(
DATA_CLIENT: client, client, device_info, software_version.get(ATTR_VERSION)
DATA_DEVICE_INFO: device_info, )
ATTR_SW_VERSION: software_version.get(ATTR_VERSION),
}
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: async def async_unload_entry(hass: HomeAssistant, entry: TwinklyConfigEntry) -> bool:
"""Remove a twinkly entry.""" """Remove a twinkly entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok

View File

@ -15,8 +15,5 @@ DEV_LED_PROFILE = "led_profile"
DEV_PROFILE_RGB = "RGB" DEV_PROFILE_RGB = "RGB"
DEV_PROFILE_RGBW = "RGBW" DEV_PROFILE_RGBW = "RGBW"
DATA_CLIENT = "client"
DATA_DEVICE_INFO = "device_info"
# Minimum version required to support effects # Minimum version required to support effects
MIN_EFFECT_VERSION = "2.7.1" MIN_EFFECT_VERSION = "2.7.1"

View File

@ -6,18 +6,18 @@ from typing import Any
from homeassistant.components.diagnostics import async_redact_data from homeassistant.components.diagnostics import async_redact_data
from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN from homeassistant.components.light import DOMAIN as LIGHT_DOMAIN
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import ATTR_SW_VERSION, CONF_HOST, CONF_IP_ADDRESS, CONF_MAC from homeassistant.const import ATTR_SW_VERSION, CONF_HOST, CONF_IP_ADDRESS, CONF_MAC
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import entity_registry as er from homeassistant.helpers import entity_registry as er
from .const import DATA_DEVICE_INFO, DOMAIN from . import TwinklyConfigEntry
from .const import DOMAIN
TO_REDACT = [CONF_HOST, CONF_IP_ADDRESS, CONF_MAC] TO_REDACT = [CONF_HOST, CONF_IP_ADDRESS, CONF_MAC]
async def async_get_config_entry_diagnostics( async def async_get_config_entry_diagnostics(
hass: HomeAssistant, entry: ConfigEntry hass: HomeAssistant, entry: TwinklyConfigEntry
) -> dict[str, Any]: ) -> dict[str, Any]:
"""Return diagnostics for a Twinkly config entry.""" """Return diagnostics for a Twinkly config entry."""
attributes = None attributes = None
@ -34,8 +34,8 @@ async def async_get_config_entry_diagnostics(
return async_redact_data( return async_redact_data(
{ {
"entry": entry.as_dict(), "entry": entry.as_dict(),
"device_info": hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_INFO], "device_info": entry.runtime_data.device_info,
ATTR_SW_VERSION: hass.data[DOMAIN][entry.entry_id][ATTR_SW_VERSION], ATTR_SW_VERSION: entry.runtime_data.sw_version,
"attributes": attributes, "attributes": attributes,
}, },
TO_REDACT, TO_REDACT,

View File

@ -7,7 +7,6 @@ from typing import Any
from aiohttp import ClientError from aiohttp import ClientError
from awesomeversion import AwesomeVersion from awesomeversion import AwesomeVersion
from ttls.client import Twinkly
from homeassistant.components.light import ( from homeassistant.components.light import (
ATTR_BRIGHTNESS, ATTR_BRIGHTNESS,
@ -18,22 +17,14 @@ from homeassistant.components.light import (
LightEntity, LightEntity,
LightEntityFeature, LightEntityFeature,
) )
from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME
from homeassistant.const import (
ATTR_SW_VERSION,
CONF_HOST,
CONF_ID,
CONF_MODEL,
CONF_NAME,
)
from homeassistant.core import HomeAssistant from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback from homeassistant.helpers.entity_platform import AddEntitiesCallback
from . import TwinklyConfigEntry
from .const import ( from .const import (
DATA_CLIENT,
DATA_DEVICE_INFO,
DEV_LED_PROFILE, DEV_LED_PROFILE,
DEV_MODEL, DEV_MODEL,
DEV_NAME, DEV_NAME,
@ -48,16 +39,11 @@ _LOGGER = logging.getLogger(__name__)
async def async_setup_entry( async def async_setup_entry(
hass: HomeAssistant, hass: HomeAssistant,
config_entry: ConfigEntry, config_entry: TwinklyConfigEntry,
async_add_entities: AddEntitiesCallback, async_add_entities: AddEntitiesCallback,
) -> None: ) -> None:
"""Setups an entity from a config entry (UI config flow).""" """Setups an entity from a config entry (UI config flow)."""
entity = TwinklyLight(config_entry)
client = hass.data[DOMAIN][config_entry.entry_id][DATA_CLIENT]
device_info = hass.data[DOMAIN][config_entry.entry_id][DATA_DEVICE_INFO]
software_version = hass.data[DOMAIN][config_entry.entry_id][ATTR_SW_VERSION]
entity = TwinklyLight(config_entry, client, device_info, software_version)
async_add_entities([entity], update_before_add=True) async_add_entities([entity], update_before_add=True)
@ -71,14 +57,12 @@ class TwinklyLight(LightEntity):
def __init__( def __init__(
self, self,
conf: ConfigEntry, entry: TwinklyConfigEntry,
client: Twinkly,
device_info,
software_version: str | None = None,
) -> None: ) -> None:
"""Initialize a TwinklyLight entity.""" """Initialize a TwinklyLight entity."""
self._attr_unique_id: str = conf.data[CONF_ID] self._attr_unique_id: str = entry.data[CONF_ID]
self._conf = conf device_info = entry.runtime_data.device_info
self._conf = entry
if device_info.get(DEV_LED_PROFILE) == DEV_PROFILE_RGBW: if device_info.get(DEV_LED_PROFILE) == DEV_PROFILE_RGBW:
self._attr_supported_color_modes = {ColorMode.RGBW} self._attr_supported_color_modes = {ColorMode.RGBW}
@ -95,18 +79,18 @@ class TwinklyLight(LightEntity):
# Those are saved in the config entry in order to have meaningful values even # Those are saved in the config entry in order to have meaningful values even
# if the device is currently offline. # if the device is currently offline.
# They are expected to be updated using the device_info. # They are expected to be updated using the device_info.
self._name = conf.data[CONF_NAME] or "Twinkly light" self._name = entry.data[CONF_NAME] or "Twinkly light"
self._model = conf.data[CONF_MODEL] self._model = entry.data[CONF_MODEL]
self._mac = device_info["mac"] self._mac = device_info["mac"]
self._client = client self._client = entry.runtime_data.client
# Set default state before any update # Set default state before any update
self._attr_is_on = False self._attr_is_on = False
self._attr_available = False self._attr_available = False
self._current_movie: dict[Any, Any] = {} self._current_movie: dict[Any, Any] = {}
self._movies: list[Any] = [] self._movies: list[Any] = []
self._software_version = software_version self._software_version = entry.runtime_data.sw_version
# We guess that most devices are "new" and support effects # We guess that most devices are "new" and support effects
self._attr_supported_features = LightEntityFeature.EFFECT self._attr_supported_features = LightEntityFeature.EFFECT