2017-05-02 16:18:47 +00:00
|
|
|
"""Helpers for Home Assistant dispatcher & internal component/platform."""
|
2017-02-25 01:11:50 +00:00
|
|
|
import logging
|
2018-11-04 21:46:42 +00:00
|
|
|
from typing import Any, Callable
|
2017-02-23 21:02:56 +00:00
|
|
|
|
|
|
|
from homeassistant.core import callback
|
2017-10-08 15:17:54 +00:00
|
|
|
from homeassistant.loader import bind_hass
|
2018-03-11 17:01:12 +00:00
|
|
|
from homeassistant.util.async_ import run_callback_threadsafe
|
2019-01-17 22:44:57 +00:00
|
|
|
from homeassistant.util.logging import catch_log_exception
|
2017-02-23 21:02:56 +00:00
|
|
|
|
2019-12-09 15:42:10 +00:00
|
|
|
from .typing import HomeAssistantType
|
2017-02-25 01:11:50 +00:00
|
|
|
|
|
|
|
_LOGGER = logging.getLogger(__name__)
|
2019-07-31 19:25:30 +00:00
|
|
|
DATA_DISPATCHER = "dispatcher"
|
2017-02-23 21:02:56 +00:00
|
|
|
|
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2019-07-31 19:25:30 +00:00
|
|
|
def dispatcher_connect(
|
|
|
|
hass: HomeAssistantType, signal: str, target: Callable[..., None]
|
|
|
|
) -> Callable[[], None]:
|
2017-06-08 13:53:12 +00:00
|
|
|
"""Connect a callable function to a signal."""
|
2017-02-25 01:11:50 +00:00
|
|
|
async_unsub = run_callback_threadsafe(
|
2019-07-31 19:25:30 +00:00
|
|
|
hass.loop, async_dispatcher_connect, hass, signal, target
|
|
|
|
).result()
|
2017-02-25 01:11:50 +00:00
|
|
|
|
2018-11-04 21:46:42 +00:00
|
|
|
def remove_dispatcher() -> None:
|
2017-02-25 01:11:50 +00:00
|
|
|
"""Remove signal listener."""
|
|
|
|
run_callback_threadsafe(hass.loop, async_unsub).result()
|
|
|
|
|
|
|
|
return remove_dispatcher
|
2017-02-23 21:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2019-07-31 19:25:30 +00:00
|
|
|
def async_dispatcher_connect(
|
|
|
|
hass: HomeAssistantType, signal: str, target: Callable[..., Any]
|
|
|
|
) -> Callable[[], None]:
|
2017-06-08 13:53:12 +00:00
|
|
|
"""Connect a callable function to a signal.
|
2017-02-23 21:02:56 +00:00
|
|
|
|
|
|
|
This method must be run in the event loop.
|
|
|
|
"""
|
|
|
|
if DATA_DISPATCHER not in hass.data:
|
|
|
|
hass.data[DATA_DISPATCHER] = {}
|
|
|
|
|
|
|
|
if signal not in hass.data[DATA_DISPATCHER]:
|
|
|
|
hass.data[DATA_DISPATCHER][signal] = []
|
|
|
|
|
2019-01-17 22:44:57 +00:00
|
|
|
wrapped_target = catch_log_exception(
|
2019-07-31 19:25:30 +00:00
|
|
|
target,
|
|
|
|
lambda *args: "Exception in {} when dispatching '{}': {}".format(
|
2020-03-27 03:44:44 +00:00
|
|
|
# Functions wrapped in partial do not have a __name__
|
|
|
|
getattr(target, "__name__", None) or str(target),
|
|
|
|
signal,
|
|
|
|
args,
|
2019-07-31 19:25:30 +00:00
|
|
|
),
|
|
|
|
)
|
2019-01-17 22:44:57 +00:00
|
|
|
|
|
|
|
hass.data[DATA_DISPATCHER][signal].append(wrapped_target)
|
2017-02-23 21:02:56 +00:00
|
|
|
|
2017-02-25 01:11:50 +00:00
|
|
|
@callback
|
2018-11-04 21:46:42 +00:00
|
|
|
def async_remove_dispatcher() -> None:
|
2017-02-25 01:11:50 +00:00
|
|
|
"""Remove signal listener."""
|
|
|
|
try:
|
2019-01-17 22:44:57 +00:00
|
|
|
hass.data[DATA_DISPATCHER][signal].remove(wrapped_target)
|
2017-02-25 01:11:50 +00:00
|
|
|
except (KeyError, ValueError):
|
|
|
|
# KeyError is key target listener did not exist
|
|
|
|
# ValueError if listener did not exist within signal
|
2019-07-31 19:25:30 +00:00
|
|
|
_LOGGER.warning("Unable to remove unknown dispatcher %s", target)
|
2017-02-25 01:11:50 +00:00
|
|
|
|
|
|
|
return async_remove_dispatcher
|
|
|
|
|
2017-02-23 21:02:56 +00:00
|
|
|
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2018-11-04 21:46:42 +00:00
|
|
|
def dispatcher_send(hass: HomeAssistantType, signal: str, *args: Any) -> None:
|
2017-02-23 21:02:56 +00:00
|
|
|
"""Send signal and data."""
|
2017-03-09 14:31:43 +00:00
|
|
|
hass.loop.call_soon_threadsafe(async_dispatcher_send, hass, signal, *args)
|
2017-02-23 21:02:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
@callback
|
2017-10-08 15:17:54 +00:00
|
|
|
@bind_hass
|
2019-07-31 19:25:30 +00:00
|
|
|
def async_dispatcher_send(hass: HomeAssistantType, signal: str, *args: Any) -> None:
|
2017-02-23 21:02:56 +00:00
|
|
|
"""Send signal and data.
|
|
|
|
|
|
|
|
This method must be run in the event loop.
|
|
|
|
"""
|
|
|
|
target_list = hass.data.get(DATA_DISPATCHER, {}).get(signal, [])
|
|
|
|
|
|
|
|
for target in target_list:
|
|
|
|
hass.async_add_job(target, *args)
|