1 """Component to create an interface to a Pilight daemon."""
3 from __future__
import annotations
5 from collections.abc
import Callable
6 from datetime
import timedelta
10 from typing
import Any
12 from pilight
import pilight
13 import voluptuous
as vol
20 EVENT_HOMEASSISTANT_START,
21 EVENT_HOMEASSISTANT_STOP,
29 _LOGGER = logging.getLogger(__name__)
31 CONF_SEND_DELAY =
"send_delay"
33 DEFAULT_HOST =
"127.0.0.1"
35 DEFAULT_SEND_DELAY = 0.0
38 EVENT =
"pilight_received"
43 RF_CODE_SCHEMA = vol.Schema(
44 {vol.Required(CONF_PROTOCOL): vol.All(cv.ensure_list, [cv.string])},
45 extra=vol.ALLOW_EXTRA,
50 CONFIG_SCHEMA = vol.Schema(
54 vol.Optional(CONF_HOST, default=DEFAULT_HOST): cv.string,
55 vol.Optional(CONF_PORT, default=DEFAULT_PORT): cv.port,
56 vol.Optional(CONF_WHITELIST, default={}): {cv.string: [cv.string]},
57 vol.Optional(CONF_SEND_DELAY, default=DEFAULT_SEND_DELAY): vol.Coerce(
63 extra=vol.ALLOW_EXTRA,
67 def setup(hass: HomeAssistant, config: ConfigType) -> bool:
68 """Set up the Pilight component."""
70 host = config[DOMAIN][CONF_HOST]
71 port = config[DOMAIN][CONF_PORT]
75 pilight_client = pilight.Client(host=host, port=port)
76 except (OSError, TimeoutError)
as err:
77 _LOGGER.error(
"Unable to connect to %s on port %s: %s", host, port, err)
80 def start_pilight_client(_):
81 """Run when Home Assistant starts."""
82 pilight_client.start()
84 hass.bus.listen_once(EVENT_HOMEASSISTANT_START, start_pilight_client)
86 def stop_pilight_client(_):
87 """Run once when Home Assistant stops."""
90 hass.bus.listen_once(EVENT_HOMEASSISTANT_STOP, stop_pilight_client)
92 @send_throttler.limited
93 def send_code(call: ServiceCall) ->
None:
94 """Send RF code to the pilight-daemon."""
97 message_data =
dict(call.data)
100 pilight_client.send_code(message_data)
102 _LOGGER.error(
"Pilight send failed for %s",
str(message_data))
104 hass.services.register(DOMAIN, SERVICE_NAME, send_code, schema=RF_CODE_SCHEMA)
108 whitelist = config[DOMAIN].
get(CONF_WHITELIST)
110 def handle_received_code(data):
111 """Run when RF codes are received."""
115 {
"protocol": data[
"protocol"],
"uuid": data[
"uuid"]}, **data[
"message"]
119 if not whitelist
or all(
str(data[key])
in whitelist[key]
for key
in whitelist):
120 hass.bus.fire(EVENT, data)
122 pilight_client.set_callback(handle_received_code)
128 """Helper class to provide service call rate throttling.
130 This class provides a decorator to decorate service methods that need
131 to be throttled to not exceed a certain call rate per second.
132 One instance can be used on multiple service methods to archive
133 an overall throttling.
135 As this uses track_point_in_utc_time to schedule delayed executions
136 it should not block the mainloop.
139 def __init__(self, hass: HomeAssistant, delay_seconds: float) ->
None:
140 """Initialize the delay handler."""
142 self._queue: list[Callable[[Any],
None]] = []
146 self.
_schedule_schedule = functools.partial(track_point_in_utc_time, hass)
148 def limited[**_P](self, method: Callable[_P, Any]) -> Callable[_P,
None]:
149 """Decorate to delay calls on a certain method."""
151 @functools.wraps(method)
152 def decorated(*args: _P.args, **kwargs: _P.kwargs) ->
None:
154 if self.
_delay_delay.total_seconds() == 0.0:
155 method(*args, **kwargs)
158 def action(event: Any) ->
None:
159 """Wrap an action that gets scheduled."""
160 method(*args, **kwargs)
162 with self.
_lock_lock:
168 next_action = self._queue.pop(0)
171 with self.
_lock_lock:
173 self._queue.append(action)
176 schedule_ts =
max(dt_util.utcnow(), self.
_next_ts_next_ts)
177 self.
_schedule_schedule(action, schedule_ts)
None __init__(self, HomeAssistant hass, float delay_seconds)
web.Response get(self, web.Request request, str config_key)
bool setup(HomeAssistant hass, ConfigType config)