forked from home-assistant/core
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathdispatcher.py
88 lines (68 loc) · 2.63 KB
/
dispatcher.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
"""Helpers for Home Assistant dispatcher & internal component/platform."""
from __future__ import annotations
from collections.abc import Callable
import logging
from typing import Any
from homeassistant.core import HassJob, HomeAssistant, callback
from homeassistant.loader import bind_hass
from homeassistant.util.async_ import run_callback_threadsafe
from homeassistant.util.logging import catch_log_exception
_LOGGER = logging.getLogger(__name__)
DATA_DISPATCHER = "dispatcher"
@bind_hass
def dispatcher_connect(
hass: HomeAssistant, signal: str, target: Callable[..., None]
) -> Callable[[], None]:
"""Connect a callable function to a signal."""
async_unsub = run_callback_threadsafe(
hass.loop, async_dispatcher_connect, hass, signal, target
).result()
def remove_dispatcher() -> None:
"""Remove signal listener."""
run_callback_threadsafe(hass.loop, async_unsub).result()
return remove_dispatcher
@callback
@bind_hass
def async_dispatcher_connect(
hass: HomeAssistant, signal: str, target: Callable[..., Any]
) -> Callable[[], None]:
"""Connect a callable function to a signal.
This method must be run in the event loop.
"""
if DATA_DISPATCHER not in hass.data:
hass.data[DATA_DISPATCHER] = {}
job = HassJob(
catch_log_exception(
target,
lambda *args: "Exception in {} when dispatching '{}': {}".format(
# Functions wrapped in partial do not have a __name__
getattr(target, "__name__", None) or str(target),
signal,
args,
),
)
)
hass.data[DATA_DISPATCHER].setdefault(signal, []).append(job)
@callback
def async_remove_dispatcher() -> None:
"""Remove signal listener."""
try:
hass.data[DATA_DISPATCHER][signal].remove(job)
except (KeyError, ValueError):
# KeyError is key target listener did not exist
# ValueError if listener did not exist within signal
_LOGGER.warning("Unable to remove unknown dispatcher %s", target)
return async_remove_dispatcher
@bind_hass
def dispatcher_send(hass: HomeAssistant, signal: str, *args: Any) -> None:
"""Send signal and data."""
hass.loop.call_soon_threadsafe(async_dispatcher_send, hass, signal, *args)
@callback
@bind_hass
def async_dispatcher_send(hass: HomeAssistant, signal: str, *args: Any) -> None:
"""Send signal and data.
This method must be run in the event loop.
"""
target_list = hass.data.get(DATA_DISPATCHER, {}).get(signal, [])
for job in target_list:
hass.async_add_hass_job(job, *args)