|
7 | 7 | from datetime import datetime, timedelta |
8 | 8 | from enum import Enum |
9 | 9 | import logging |
10 | | -from typing import cast |
| 10 | +from typing import Any, cast |
11 | 11 |
|
12 | 12 | from hass_nabucasa import Cloud |
13 | 13 | import voluptuous as vol |
|
85 | 85 | "CLOUD_CONNECTION_STATE" |
86 | 86 | ) |
87 | 87 |
|
| 88 | +_SIGNAL_CLOUDHOOKS_UPDATED: SignalType[dict[str, Any]] = SignalType( |
| 89 | + "CLOUDHOOKS_UPDATED" |
| 90 | +) |
| 91 | + |
88 | 92 | STARTUP_REPAIR_DELAY = 1 # 1 hour |
89 | 93 |
|
90 | 94 | ALEXA_ENTITY_SCHEMA = vol.Schema( |
@@ -240,6 +244,24 @@ async def async_delete_cloudhook(hass: HomeAssistant, webhook_id: str) -> None: |
240 | 244 | await hass.data[DATA_CLOUD].cloudhooks.async_delete(webhook_id) |
241 | 245 |
|
242 | 246 |
|
| 247 | +@callback |
| 248 | +def async_listen_cloudhook_change( |
| 249 | + hass: HomeAssistant, |
| 250 | + webhook_id: str, |
| 251 | + on_change: Callable[[dict[str, Any] | None], None], |
| 252 | +) -> Callable[[], None]: |
| 253 | + """Listen for cloudhook changes for the given webhook and notify when modified or deleted.""" |
| 254 | + |
| 255 | + @callback |
| 256 | + def _handle_cloudhooks_updated(cloudhooks: dict[str, Any]) -> None: |
| 257 | + """Handle cloudhooks updated signal.""" |
| 258 | + on_change(cloudhooks.get(webhook_id)) |
| 259 | + |
| 260 | + return async_dispatcher_connect( |
| 261 | + hass, _SIGNAL_CLOUDHOOKS_UPDATED, _handle_cloudhooks_updated |
| 262 | + ) |
| 263 | + |
| 264 | + |
243 | 265 | @bind_hass |
244 | 266 | @callback |
245 | 267 | def async_remote_ui_url(hass: HomeAssistant) -> str: |
@@ -287,7 +309,7 @@ async def _shutdown(event: Event) -> None: |
287 | 309 |
|
288 | 310 | hass.bus.async_listen_once(EVENT_HOMEASSISTANT_STOP, _shutdown) |
289 | 311 |
|
290 | | - _remote_handle_prefs_updated(cloud) |
| 312 | + _handle_prefs_updated(hass, cloud) |
291 | 313 | _setup_services(hass, prefs) |
292 | 314 |
|
293 | 315 | async def async_startup_repairs(_: datetime) -> None: |
@@ -371,26 +393,32 @@ async def _on_initialized() -> None: |
371 | 393 |
|
372 | 394 |
|
373 | 395 | @callback |
374 | | -def _remote_handle_prefs_updated(cloud: Cloud[CloudClient]) -> None: |
375 | | - """Handle remote preferences updated.""" |
376 | | - cur_pref = cloud.client.prefs.remote_enabled |
| 396 | +def _handle_prefs_updated(hass: HomeAssistant, cloud: Cloud[CloudClient]) -> None: |
| 397 | + """Register handler for cloud preferences updates.""" |
| 398 | + cur_remote_enabled = cloud.client.prefs.remote_enabled |
| 399 | + cur_cloudhooks = cloud.client.prefs.cloudhooks |
377 | 400 | lock = asyncio.Lock() |
378 | 401 |
|
379 | | - # Sync remote connection with prefs |
380 | | - async def remote_prefs_updated(prefs: CloudPreferences) -> None: |
381 | | - """Update remote status.""" |
382 | | - nonlocal cur_pref |
| 402 | + async def on_prefs_updated(prefs: CloudPreferences) -> None: |
| 403 | + """Handle cloud preferences updates.""" |
| 404 | + nonlocal cur_remote_enabled |
| 405 | + nonlocal cur_cloudhooks |
383 | 406 |
|
| 407 | + # Lock protects cur_ state variables from concurrent updates |
384 | 408 | async with lock: |
385 | | - if prefs.remote_enabled == cur_pref: |
| 409 | + if cur_cloudhooks != prefs.cloudhooks: |
| 410 | + cur_cloudhooks = prefs.cloudhooks |
| 411 | + async_dispatcher_send(hass, _SIGNAL_CLOUDHOOKS_UPDATED, cur_cloudhooks) |
| 412 | + |
| 413 | + if prefs.remote_enabled == cur_remote_enabled: |
386 | 414 | return |
387 | 415 |
|
388 | | - if cur_pref := prefs.remote_enabled: |
| 416 | + if cur_remote_enabled := prefs.remote_enabled: |
389 | 417 | await cloud.remote.connect() |
390 | 418 | else: |
391 | 419 | await cloud.remote.disconnect() |
392 | 420 |
|
393 | | - cloud.client.prefs.async_listen_updates(remote_prefs_updated) |
| 421 | + cloud.client.prefs.async_listen_updates(on_prefs_updated) |
394 | 422 |
|
395 | 423 |
|
396 | 424 | async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: |
|
0 commit comments