From be3d65538dea0e59a3b7cb3e56683964c4da05e2 Mon Sep 17 00:00:00 2001 From: epenet <6771947+epenet@users.noreply.github.com> Date: Mon, 30 Mar 2026 12:32:27 +0200 Subject: [PATCH] Use runtime_data in motion_blinds integration (#166849) Co-authored-by: Claude Opus 4.6 (1M context) --- .../components/motion_blinds/__init__.py | 35 +++++-------------- .../components/motion_blinds/button.py | 10 +++--- .../components/motion_blinds/config_flow.py | 4 +-- .../components/motion_blinds/const.py | 1 - .../components/motion_blinds/coordinator.py | 28 +++++++++------ .../components/motion_blinds/cover.py | 10 +++--- .../components/motion_blinds/sensor.py | 9 +++-- 7 files changed, 40 insertions(+), 57 deletions(-) diff --git a/homeassistant/components/motion_blinds/__init__.py b/homeassistant/components/motion_blinds/__init__.py index 9c4d1a97f00..a13a73e6f90 100644 --- a/homeassistant/components/motion_blinds/__init__.py +++ b/homeassistant/components/motion_blinds/__init__.py @@ -2,11 +2,9 @@ import asyncio import logging -from typing import TYPE_CHECKING from motionblinds import AsyncMotionMulticast -from homeassistant.config_entries import ConfigEntry from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP from homeassistant.core import HomeAssistant from homeassistant.exceptions import ConfigEntryNotReady @@ -14,32 +12,28 @@ from homeassistant.exceptions import ConfigEntryNotReady from .const import ( CONF_BLIND_TYPE_LIST, CONF_INTERFACE, - CONF_WAIT_FOR_PUSH, DEFAULT_INTERFACE, - DEFAULT_WAIT_FOR_PUSH, DOMAIN, - KEY_API_LOCK, - KEY_COORDINATOR, - KEY_GATEWAY, KEY_MULTICAST_LISTENER, KEY_SETUP_LOCK, KEY_UNSUB_STOP, PLATFORMS, ) -from .coordinator import DataUpdateCoordinatorMotionBlinds +from .coordinator import DataUpdateCoordinatorMotionBlinds, MotionBlindsConfigEntry from .gateway import ConnectMotionGateway _LOGGER = logging.getLogger(__name__) -async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: +async def async_setup_entry( + hass: HomeAssistant, entry: MotionBlindsConfigEntry +) -> bool: """Set up the motion_blinds components from a config entry.""" hass.data.setdefault(DOMAIN, {}) setup_lock = hass.data[DOMAIN].setdefault(KEY_SETUP_LOCK, asyncio.Lock()) host = entry.data[CONF_HOST] key = entry.data[CONF_API_KEY] multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE) - wait_for_push = entry.options.get(CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH) blind_type_list = entry.data.get(CONF_BLIND_TYPE_LIST) # Create multicast Listener @@ -88,15 +82,9 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: ): raise ConfigEntryNotReady motion_gateway = connect_gateway_class.gateway_device - api_lock = asyncio.Lock() - coordinator_info = { - KEY_GATEWAY: motion_gateway, - KEY_API_LOCK: api_lock, - CONF_WAIT_FOR_PUSH: wait_for_push, - } coordinator = DataUpdateCoordinatorMotionBlinds( - hass, entry, _LOGGER, coordinator_info + hass, entry, _LOGGER, motion_gateway ) # store blind type list for next time @@ -110,20 +98,16 @@ async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: # Fetch initial data so we have data when entities subscribe await coordinator.async_config_entry_first_refresh() - hass.data[DOMAIN][entry.entry_id] = { - KEY_GATEWAY: motion_gateway, - KEY_COORDINATOR: coordinator, - } - - if TYPE_CHECKING: - assert entry.unique_id is not None + entry.runtime_data = coordinator await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) return True -async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool: +async def async_unload_entry( + hass: HomeAssistant, config_entry: MotionBlindsConfigEntry +) -> bool: """Unload a config entry.""" unload_ok = await hass.config_entries.async_unload_platforms( config_entry, PLATFORMS @@ -132,7 +116,6 @@ async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> if unload_ok: multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER] multicast.Unregister_motion_gateway(config_entry.data[CONF_HOST]) - hass.data[DOMAIN].pop(config_entry.entry_id) if not hass.config_entries.async_loaded_entries(DOMAIN): # No motion gateways left, stop Motion multicast diff --git a/homeassistant/components/motion_blinds/button.py b/homeassistant/components/motion_blinds/button.py index 09f29e09c70..f590f50694c 100644 --- a/homeassistant/components/motion_blinds/button.py +++ b/homeassistant/components/motion_blinds/button.py @@ -5,25 +5,23 @@ from __future__ import annotations from motionblinds.motion_blinds import LimitStatus, MotionBlind from homeassistant.components.button import ButtonEntity -from homeassistant.config_entries import ConfigEntry from homeassistant.const import EntityCategory from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN, KEY_COORDINATOR, KEY_GATEWAY -from .coordinator import DataUpdateCoordinatorMotionBlinds +from .coordinator import DataUpdateCoordinatorMotionBlinds, MotionBlindsConfigEntry from .entity import MotionCoordinatorEntity async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: MotionBlindsConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Perform the setup for Motionblinds.""" entities: list[ButtonEntity] = [] - motion_gateway = hass.data[DOMAIN][config_entry.entry_id][KEY_GATEWAY] - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] + coordinator = config_entry.runtime_data + motion_gateway = coordinator.gateway for blind in motion_gateway.device_list.values(): if blind.limit_status in ( diff --git a/homeassistant/components/motion_blinds/config_flow.py b/homeassistant/components/motion_blinds/config_flow.py index cd85de5c627..59a65aab001 100644 --- a/homeassistant/components/motion_blinds/config_flow.py +++ b/homeassistant/components/motion_blinds/config_flow.py @@ -9,7 +9,6 @@ from motionblinds import MotionDiscovery, MotionGateway import voluptuous as vol from homeassistant.config_entries import ( - ConfigEntry, ConfigFlow, ConfigFlowResult, OptionsFlowWithReload, @@ -27,6 +26,7 @@ from .const import ( DEFAULT_WAIT_FOR_PUSH, DOMAIN, ) +from .coordinator import MotionBlindsConfigEntry from .gateway import ConnectMotionGateway _LOGGER = logging.getLogger(__name__) @@ -79,7 +79,7 @@ class MotionBlindsFlowHandler(ConfigFlow, domain=DOMAIN): @staticmethod @callback def async_get_options_flow( - config_entry: ConfigEntry, + config_entry: MotionBlindsConfigEntry, ) -> OptionsFlowHandler: """Get the options flow.""" return OptionsFlowHandler() diff --git a/homeassistant/components/motion_blinds/const.py b/homeassistant/components/motion_blinds/const.py index 950fa3ab4c7..1d151a1e63b 100644 --- a/homeassistant/components/motion_blinds/const.py +++ b/homeassistant/components/motion_blinds/const.py @@ -16,7 +16,6 @@ DEFAULT_INTERFACE = "any" KEY_GATEWAY = "gateway" KEY_API_LOCK = "api_lock" -KEY_COORDINATOR = "coordinator" KEY_MULTICAST_LISTENER = "multicast_listener" KEY_SETUP_LOCK = "setup_lock" KEY_UNSUB_STOP = "unsub_stop" diff --git a/homeassistant/components/motion_blinds/coordinator.py b/homeassistant/components/motion_blinds/coordinator.py index 8de793c405f..6614b666538 100644 --- a/homeassistant/components/motion_blinds/coordinator.py +++ b/homeassistant/components/motion_blinds/coordinator.py @@ -1,11 +1,12 @@ """DataUpdateCoordinator for Motionblinds integration.""" +from __future__ import annotations + import asyncio from datetime import timedelta import logging -from typing import Any -from motionblinds import DEVICE_TYPES_WIFI, ParseException +from motionblinds import DEVICE_TYPES_WIFI, MotionGateway, ParseException from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant @@ -14,7 +15,7 @@ from homeassistant.helpers.update_coordinator import DataUpdateCoordinator from .const import ( ATTR_AVAILABLE, CONF_WAIT_FOR_PUSH, - KEY_API_LOCK, + DEFAULT_WAIT_FOR_PUSH, KEY_GATEWAY, UPDATE_INTERVAL, UPDATE_INTERVAL_FAST, @@ -23,17 +24,20 @@ from .const import ( _LOGGER = logging.getLogger(__name__) +type MotionBlindsConfigEntry = ConfigEntry[DataUpdateCoordinatorMotionBlinds] + + class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator): """Class to manage fetching data from single endpoint.""" - config_entry: ConfigEntry + config_entry: MotionBlindsConfigEntry def __init__( self, hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: MotionBlindsConfigEntry, logger: logging.Logger, - coordinator_info: dict[str, Any], + gateway: MotionGateway, ) -> None: """Initialize global data updater.""" super().__init__( @@ -44,14 +48,16 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator): update_interval=timedelta(seconds=UPDATE_INTERVAL), ) - self.api_lock = coordinator_info[KEY_API_LOCK] - self._gateway = coordinator_info[KEY_GATEWAY] - self._wait_for_push = coordinator_info[CONF_WAIT_FOR_PUSH] + self.api_lock = asyncio.Lock() + self.gateway = gateway + self._wait_for_push = config_entry.options.get( + CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH + ) def update_gateway(self): """Fetch data from gateway.""" try: - self._gateway.Update() + self.gateway.Update() except TimeoutError, ParseException: # let the error be logged and handled by the motionblinds library return {ATTR_AVAILABLE: False} @@ -82,7 +88,7 @@ class DataUpdateCoordinatorMotionBlinds(DataUpdateCoordinator): self.update_gateway ) - for blind in self._gateway.device_list.values(): + for blind in self.gateway.device_list.values(): await asyncio.sleep(1.5) async with self.api_lock: data[blind.mac] = await self.hass.async_add_executor_job( diff --git a/homeassistant/components/motion_blinds/cover.py b/homeassistant/components/motion_blinds/cover.py index f1351af8bc2..342a00686d6 100644 --- a/homeassistant/components/motion_blinds/cover.py +++ b/homeassistant/components/motion_blinds/cover.py @@ -15,7 +15,6 @@ from homeassistant.components.cover import ( CoverEntity, CoverEntityFeature, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.core import HomeAssistant from homeassistant.helpers import config_validation as cv, entity_platform from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback @@ -25,12 +24,11 @@ from .const import ( ATTR_ABSOLUTE_POSITION, ATTR_AVAILABLE, ATTR_WIDTH, - DOMAIN, - KEY_COORDINATOR, KEY_GATEWAY, SERVICE_SET_ABSOLUTE_POSITION, UPDATE_DELAY_STOP, ) +from .coordinator import MotionBlindsConfigEntry from .entity import MotionCoordinatorEntity _LOGGER = logging.getLogger(__name__) @@ -84,13 +82,13 @@ SET_ABSOLUTE_POSITION_SCHEMA: VolDictType = { async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: MotionBlindsConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Set up the Motion Blind from a config entry.""" entities: list[MotionBaseDevice] = [] - motion_gateway = hass.data[DOMAIN][config_entry.entry_id][KEY_GATEWAY] - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] + coordinator = config_entry.runtime_data + motion_gateway = coordinator.gateway for blind in motion_gateway.device_list.values(): if blind.type in POSITION_DEVICE_MAP: diff --git a/homeassistant/components/motion_blinds/sensor.py b/homeassistant/components/motion_blinds/sensor.py index eac89eccdd2..673cbc5458e 100644 --- a/homeassistant/components/motion_blinds/sensor.py +++ b/homeassistant/components/motion_blinds/sensor.py @@ -10,7 +10,6 @@ from homeassistant.components.sensor import ( SensorEntity, SensorStateClass, ) -from homeassistant.config_entries import ConfigEntry from homeassistant.const import ( PERCENTAGE, SIGNAL_STRENGTH_DECIBELS_MILLIWATT, @@ -19,7 +18,7 @@ from homeassistant.const import ( from homeassistant.core import HomeAssistant from homeassistant.helpers.entity_platform import AddConfigEntryEntitiesCallback -from .const import DOMAIN, KEY_COORDINATOR, KEY_GATEWAY +from .coordinator import MotionBlindsConfigEntry from .entity import MotionCoordinatorEntity ATTR_BATTERY_VOLTAGE = "battery_voltage" @@ -27,13 +26,13 @@ ATTR_BATTERY_VOLTAGE = "battery_voltage" async def async_setup_entry( hass: HomeAssistant, - config_entry: ConfigEntry, + config_entry: MotionBlindsConfigEntry, async_add_entities: AddConfigEntryEntitiesCallback, ) -> None: """Perform the setup for Motionblinds.""" entities: list[SensorEntity] = [] - motion_gateway = hass.data[DOMAIN][config_entry.entry_id][KEY_GATEWAY] - coordinator = hass.data[DOMAIN][config_entry.entry_id][KEY_COORDINATOR] + coordinator = config_entry.runtime_data + motion_gateway = coordinator.gateway for blind in motion_gateway.device_list.values(): entities.append(MotionSignalStrengthSensor(coordinator, blind))