diff --git a/homeassistant/components/twinkly/__init__.py b/homeassistant/components/twinkly/__init__.py index b09e58ff12f..00e40d604c0 100644 --- a/homeassistant/components/twinkly/__init__.py +++ b/homeassistant/components/twinkly/__init__.py @@ -1,29 +1,40 @@ """The twinkly component.""" +from dataclasses import dataclass +from typing import Any + from aiohttp import ClientError from ttls.client import Twinkly 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.exceptions import ConfigEntryNotReady 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] -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: - """Set up entries from config flow.""" - hass.data.setdefault(DOMAIN, {}) +@dataclass +class TwinklyData: + """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 will be able to properly share the connection. host = entry.data[CONF_HOST] - hass.data[DOMAIN].setdefault(entry.entry_id, {}) - client = Twinkly(host, async_get_clientsession(hass)) try: @@ -32,21 +43,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: except (TimeoutError, ClientError) as exception: raise ConfigEntryNotReady from exception - hass.data[DOMAIN][entry.entry_id] = { - DATA_CLIENT: client, - DATA_DEVICE_INFO: device_info, - ATTR_SW_VERSION: software_version.get(ATTR_VERSION), - } + entry.runtime_data = TwinklyData( + client, device_info, software_version.get(ATTR_VERSION) + ) + await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) 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.""" - unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS) - if unload_ok: - hass.data[DOMAIN].pop(entry.entry_id) - - return unload_ok + return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) diff --git a/homeassistant/components/twinkly/const.py b/homeassistant/components/twinkly/const.py index f33024ed156..488b213b895 100644 --- a/homeassistant/components/twinkly/const.py +++ b/homeassistant/components/twinkly/const.py @@ -15,8 +15,5 @@ DEV_LED_PROFILE = "led_profile" DEV_PROFILE_RGB = "RGB" DEV_PROFILE_RGBW = "RGBW" -DATA_CLIENT = "client" -DATA_DEVICE_INFO = "device_info" - # Minimum version required to support effects MIN_EFFECT_VERSION = "2.7.1" diff --git a/homeassistant/components/twinkly/diagnostics.py b/homeassistant/components/twinkly/diagnostics.py index e188e92ecd5..9ddc65cf255 100644 --- a/homeassistant/components/twinkly/diagnostics.py +++ b/homeassistant/components/twinkly/diagnostics.py @@ -6,18 +6,18 @@ from typing import Any from homeassistant.components.diagnostics import async_redact_data 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.core import HomeAssistant 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] async def async_get_config_entry_diagnostics( - hass: HomeAssistant, entry: ConfigEntry + hass: HomeAssistant, entry: TwinklyConfigEntry ) -> dict[str, Any]: """Return diagnostics for a Twinkly config entry.""" attributes = None @@ -34,8 +34,8 @@ async def async_get_config_entry_diagnostics( return async_redact_data( { "entry": entry.as_dict(), - "device_info": hass.data[DOMAIN][entry.entry_id][DATA_DEVICE_INFO], - ATTR_SW_VERSION: hass.data[DOMAIN][entry.entry_id][ATTR_SW_VERSION], + "device_info": entry.runtime_data.device_info, + ATTR_SW_VERSION: entry.runtime_data.sw_version, "attributes": attributes, }, TO_REDACT, diff --git a/homeassistant/components/twinkly/light.py b/homeassistant/components/twinkly/light.py index 771af2282dc..d05da7bab15 100644 --- a/homeassistant/components/twinkly/light.py +++ b/homeassistant/components/twinkly/light.py @@ -7,7 +7,6 @@ from typing import Any from aiohttp import ClientError from awesomeversion import AwesomeVersion -from ttls.client import Twinkly from homeassistant.components.light import ( ATTR_BRIGHTNESS, @@ -18,22 +17,14 @@ from homeassistant.components.light import ( LightEntity, LightEntityFeature, ) -from homeassistant.config_entries import ConfigEntry -from homeassistant.const import ( - ATTR_SW_VERSION, - CONF_HOST, - CONF_ID, - CONF_MODEL, - CONF_NAME, -) +from homeassistant.const import CONF_HOST, CONF_ID, CONF_MODEL, CONF_NAME from homeassistant.core import HomeAssistant from homeassistant.helpers import device_registry as dr from homeassistant.helpers.device_registry import CONNECTION_NETWORK_MAC, DeviceInfo from homeassistant.helpers.entity_platform import AddEntitiesCallback +from . import TwinklyConfigEntry from .const import ( - DATA_CLIENT, - DATA_DEVICE_INFO, DEV_LED_PROFILE, DEV_MODEL, DEV_NAME, @@ -48,16 +39,11 @@ _LOGGER = logging.getLogger(__name__) async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: TwinklyConfigEntry, async_add_entities: AddEntitiesCallback, ) -> None: """Setups an entity from a config entry (UI config flow).""" - - 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) + entity = TwinklyLight(config_entry) async_add_entities([entity], update_before_add=True) @@ -71,14 +57,12 @@ class TwinklyLight(LightEntity): def __init__( self, - conf: ConfigEntry, - client: Twinkly, - device_info, - software_version: str | None = None, + entry: TwinklyConfigEntry, ) -> None: """Initialize a TwinklyLight entity.""" - self._attr_unique_id: str = conf.data[CONF_ID] - self._conf = conf + self._attr_unique_id: str = entry.data[CONF_ID] + device_info = entry.runtime_data.device_info + self._conf = entry if device_info.get(DEV_LED_PROFILE) == DEV_PROFILE_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 # if the device is currently offline. # They are expected to be updated using the device_info. - self._name = conf.data[CONF_NAME] or "Twinkly light" - self._model = conf.data[CONF_MODEL] + self._name = entry.data[CONF_NAME] or "Twinkly light" + self._model = entry.data[CONF_MODEL] self._mac = device_info["mac"] - self._client = client + self._client = entry.runtime_data.client # Set default state before any update self._attr_is_on = False self._attr_available = False self._current_movie: dict[Any, 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 self._attr_supported_features = LightEntityFeature.EFFECT