Home Assistant Unofficial Reference 2024.12.1
__init__.py
Go to the documentation of this file.
1 """The motion_blinds component."""
2 
3 import asyncio
4 from datetime import timedelta
5 import logging
6 from typing import TYPE_CHECKING
7 
8 from motionblinds import AsyncMotionMulticast
9 
10 from homeassistant.config_entries import ConfigEntry, ConfigEntryState
11 from homeassistant.const import CONF_API_KEY, CONF_HOST, EVENT_HOMEASSISTANT_STOP
12 from homeassistant.core import HomeAssistant
13 from homeassistant.exceptions import ConfigEntryNotReady
14 
15 from .const import (
16  CONF_INTERFACE,
17  CONF_WAIT_FOR_PUSH,
18  DEFAULT_INTERFACE,
19  DEFAULT_WAIT_FOR_PUSH,
20  DOMAIN,
21  KEY_API_LOCK,
22  KEY_COORDINATOR,
23  KEY_GATEWAY,
24  KEY_MULTICAST_LISTENER,
25  KEY_SETUP_LOCK,
26  KEY_UNSUB_STOP,
27  PLATFORMS,
28  UPDATE_INTERVAL,
29 )
30 from .coordinator import DataUpdateCoordinatorMotionBlinds
31 from .gateway import ConnectMotionGateway
32 
33 _LOGGER = logging.getLogger(__name__)
34 
35 
36 async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
37  """Set up the motion_blinds components from a config entry."""
38  hass.data.setdefault(DOMAIN, {})
39  setup_lock = hass.data[DOMAIN].setdefault(KEY_SETUP_LOCK, asyncio.Lock())
40  host = entry.data[CONF_HOST]
41  key = entry.data[CONF_API_KEY]
42  multicast_interface = entry.data.get(CONF_INTERFACE, DEFAULT_INTERFACE)
43  wait_for_push = entry.options.get(CONF_WAIT_FOR_PUSH, DEFAULT_WAIT_FOR_PUSH)
44 
45  # Create multicast Listener
46  async with setup_lock:
47  if KEY_MULTICAST_LISTENER not in hass.data[DOMAIN]:
48  # check multicast interface
49  check_multicast_class = ConnectMotionGateway(
50  hass, interface=multicast_interface
51  )
52  working_interface = await check_multicast_class.async_check_interface(
53  host, key
54  )
55  if working_interface != multicast_interface:
56  data = {**entry.data, CONF_INTERFACE: working_interface}
57  hass.config_entries.async_update_entry(entry, data=data)
58  _LOGGER.debug(
59  (
60  "Motionblinds interface updated from %s to %s, "
61  "this should only occur after a network change"
62  ),
63  multicast_interface,
64  working_interface,
65  )
66 
67  multicast = AsyncMotionMulticast(interface=working_interface)
68  hass.data[DOMAIN][KEY_MULTICAST_LISTENER] = multicast
69  # start listening for local pushes (only once)
70  await multicast.Start_listen()
71 
72  # register stop callback to shutdown listening for local pushes
73  def stop_motion_multicast(event):
74  """Stop multicast thread."""
75  _LOGGER.debug("Shutting down Motion Listener")
76  multicast.Stop_listen()
77 
78  unsub = hass.bus.async_listen_once(
79  EVENT_HOMEASSISTANT_STOP, stop_motion_multicast
80  )
81  hass.data[DOMAIN][KEY_UNSUB_STOP] = unsub
82 
83  # Connect to motion gateway
84  multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER]
85  connect_gateway_class = ConnectMotionGateway(hass, multicast)
86  if not await connect_gateway_class.async_connect_gateway(host, key):
87  raise ConfigEntryNotReady
88  motion_gateway = connect_gateway_class.gateway_device
89  api_lock = asyncio.Lock()
90  coordinator_info = {
91  KEY_GATEWAY: motion_gateway,
92  KEY_API_LOCK: api_lock,
93  CONF_WAIT_FOR_PUSH: wait_for_push,
94  }
95 
97  hass,
98  _LOGGER,
99  coordinator_info,
100  # Name of the data. For logging purposes.
101  name=entry.title,
102  # Polling interval. Will only be polled if there are subscribers.
103  update_interval=timedelta(seconds=UPDATE_INTERVAL),
104  )
105 
106  # Fetch initial data so we have data when entities subscribe
107  await coordinator.async_config_entry_first_refresh()
108 
109  hass.data[DOMAIN][entry.entry_id] = {
110  KEY_GATEWAY: motion_gateway,
111  KEY_COORDINATOR: coordinator,
112  }
113 
114  if TYPE_CHECKING:
115  assert entry.unique_id is not None
116 
117  await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
118 
119  entry.async_on_unload(entry.add_update_listener(update_listener))
120 
121  return True
122 
123 
124 async def async_unload_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
125  """Unload a config entry."""
126  unload_ok = await hass.config_entries.async_unload_platforms(
127  config_entry, PLATFORMS
128  )
129 
130  if unload_ok:
131  multicast = hass.data[DOMAIN][KEY_MULTICAST_LISTENER]
132  multicast.Unregister_motion_gateway(config_entry.data[CONF_HOST])
133  hass.data[DOMAIN].pop(config_entry.entry_id)
134 
135  loaded_entries = [
136  entry
137  for entry in hass.config_entries.async_entries(DOMAIN)
138  if entry.state == ConfigEntryState.LOADED
139  ]
140  if len(loaded_entries) == 1:
141  # No motion gateways left, stop Motion multicast
142  unsub_stop = hass.data[DOMAIN].pop(KEY_UNSUB_STOP)
143  unsub_stop()
144  _LOGGER.debug("Shutting down Motion Listener")
145  multicast = hass.data[DOMAIN].pop(KEY_MULTICAST_LISTENER)
146  multicast.Stop_listen()
147 
148  return unload_ok
149 
150 
151 async def update_listener(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
152  """Handle options update."""
153  await hass.config_entries.async_reload(config_entry.entry_id)
bool async_unload_entry(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:124
None update_listener(HomeAssistant hass, ConfigEntry config_entry)
Definition: __init__.py:151
bool async_setup_entry(HomeAssistant hass, ConfigEntry entry)
Definition: __init__.py:36