Compare commits

...

6 Commits

Author SHA1 Message Date
epenet
b633dcf37e Simplify 2026-03-30 08:49:07 +00:00
epenet
c2aa2718a1 Update 2026-03-30 08:46:40 +00:00
epenet
e5c6ac6c4e Update 2026-03-30 08:36:08 +00:00
epenet
a6e5f77ab5 Update 2026-03-30 08:33:20 +00:00
epenet
3d3fd950dc Move MotionBlindsData and MotionBlindsConfigEntry to coordinator module
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 08:23:55 +00:00
epenet
9fb6da9099 Use runtime_data in motion_blinds integration
Migrate the motion_blinds integration to use entry.runtime_data
instead of hass.data[DOMAIN][entry.entry_id] for storing per-entry
gateway and coordinator data.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-30 08:21:05 +00:00
7 changed files with 40 additions and 57 deletions

View File

@@ -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

View File

@@ -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 (

View File

@@ -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()

View File

@@ -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"

View File

@@ -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(

View File

@@ -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:

View File

@@ -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))