Type the motion_blinds multicast listener with a HassKey (#182233)

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Yanze (David) Wu
2026-09-15 08:43:49 +02:00
committed by GitHub
co-authored by Claude Opus 5
parent 47338dc365
commit afbd203c00
2 changed files with 34 additions and 24 deletions
@@ -1,24 +1,22 @@
"""The motion_blinds component."""
# pylint: disable=home-assistant-use-runtime-data # Uses legacy hass.data[DOMAIN] pattern
import asyncio
from dataclasses import dataclass, field
import logging
from motionblinds import DEVICE_TYPES_GATEWAY, DEVICE_TYPES_WIFI, AsyncMotionMulticast
from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP
from homeassistant.core import HomeAssistant
from homeassistant.core import CALLBACK_TYPE, HomeAssistant
from homeassistant.exceptions import ConfigEntryNotReady
from homeassistant.helpers import device_registry as dr
from homeassistant.util.hass_dict import HassKey
from .const import (
CONF_BLIND_TYPE_LIST,
CONF_INTERFACE,
DEFAULT_INTERFACE,
DOMAIN,
KEY_MULTICAST_LISTENER,
KEY_SETUP_LOCK,
KEY_UNSUB_STOP,
PLATFORMS,
)
from .coordinator import DataUpdateCoordinatorMotionBlinds, MotionBlindsConfigEntry
@@ -28,20 +26,34 @@ from .gateway import ConnectMotionGateway
_LOGGER = logging.getLogger(__name__)
@dataclass
class MotionBlindsData:
"""Multicast listener shared by every gateway."""
setup_lock: asyncio.Lock = field(default_factory=asyncio.Lock)
multicast: AsyncMotionMulticast | None = None
unsub_stop: CALLBACK_TYPE | None = None
# One multicast listener serves every gateway, so it is shared between config
# entries rather than owned by any one of them.
MOTION_BLINDS_DATA: HassKey[MotionBlindsData] = HassKey(DOMAIN)
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())
if (motion_data := hass.data.get(MOTION_BLINDS_DATA)) is None:
motion_data = hass.data[MOTION_BLINDS_DATA] = MotionBlindsData()
host = entry.data[CONF_HOST]
key = entry.data[CONF_API_KEY]
multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE)
blind_type_list = entry.data.get(CONF_BLIND_TYPE_LIST)
# Create multicast Listener
async with setup_lock:
if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]:
async with motion_data.setup_lock:
if (multicast := motion_data.multicast) is None:
# check multicast interface
check_multicast_class = ConnectMotionGateway(
hass, interface=multicast_interface
@@ -61,8 +73,9 @@ async def async_setup_entry(
working_interface,
)
multicast = AsyncMotionMulticast(interface=working_interface)
hass.data[DOMAIN][KEY_MULTICAST_LISTENER] = multicast
multicast = motion_data.multicast = AsyncMotionMulticast(
interface=working_interface
)
# start listening for local pushes (only once)
await multicast.Start_listen()
@@ -72,13 +85,11 @@ async def async_setup_entry(
_LOGGER.debug("Shutting down Motion Listener")
multicast.Stop_listen()
unsub = hass.bus.async_listen_once(
motion_data.unsub_stop = hass.bus.async_listen_once(
EVENT_HOMEASSISTANT_STOP, stop_motion_multicast
)
hass.data[DOMAIN][KEY_UNSUB_STOP] = unsub
# Connect to motion gateway
multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER]
connect_gateway_class = ConnectMotionGateway(hass, multicast)
if not await connect_gateway_class.async_connect_gateway(
host, key, blind_type_list
@@ -130,16 +141,18 @@ async def async_unload_entry(
config_entry, PLATFORMS
)
if unload_ok:
multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER]
multicast.Unregister_motion_gateway(config_entry.data[CONF_HOST])
motion_data = hass.data[MOTION_BLINDS_DATA]
if unload_ok and motion_data.multicast is not None:
motion_data.multicast.Unregister_motion_gateway(config_entry.data[CONF_HOST])
if not hass.config_entries.async_loaded_entries(DOMAIN):
# No motion gateways left, stop Motion multicast
unsub_stop = hass.data[DOMAIN].pop(KEY_UNSUB_STOP)
unsub_stop()
if motion_data.unsub_stop is not None:
motion_data.unsub_stop()
motion_data.unsub_stop = None
_LOGGER.debug("Shutting down Motion Listener")
multicast = hass.data[DOMAIN].pop(KEY_MULTICAST_LISTENER)
multicast.Stop_listen()
if motion_data.multicast is not None:
motion_data.multicast.Stop_listen()
motion_data.multicast = None
return unload_ok
@@ -16,9 +16,6 @@ DEFAULT_INTERFACE = "any"
KEY_GATEWAY = "gateway"
KEY_API_LOCK = "api_lock"
KEY_MULTICAST_LISTENER = "multicast_listener"
KEY_SETUP_LOCK = "setup_lock"
KEY_UNSUB_STOP = "unsub_stop"
ATTR_WIDTH = "width"
ATTR_ABSOLUTE_POSITION = "absolute_position"