Initial Home Assistant Configuration
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Optional
|
||||
|
||||
#
|
||||
from homeassistant.core import CALLBACK_TYPE, HomeAssistant, callback, HassJob
|
||||
from homeassistant.loader import bind_hass
|
||||
from homeassistant.util import dt as dt_util
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_time_interval,
|
||||
async_track_point_in_utc_time,
|
||||
)
|
||||
|
||||
# For targeted patching in tests
|
||||
time_tracker_utcnow = dt_util.utcnow
|
||||
|
||||
|
||||
__ALL__ = ["async_track_time_change_in_tz"]
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
def async_track_utc_time_change(
|
||||
hass: HomeAssistant,
|
||||
action: None,
|
||||
hour: Optional[Any] = None,
|
||||
minute: Optional[Any] = None,
|
||||
second: Optional[Any] = None,
|
||||
tz: Optional[Any] = None,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Add a listener that will fire if time matches a pattern."""
|
||||
# This is function is modifies to support timezones.
|
||||
|
||||
# We do not have to wrap the function with time pattern matching logic
|
||||
# if no pattern given
|
||||
if all(val is None for val in (hour, minute, second)):
|
||||
# Previously this relied on EVENT_TIME_FIRED
|
||||
# which meant it would not fire right away because
|
||||
# the caller would always be misaligned with the call
|
||||
# time vs the fire time by < 1s. To preserve this
|
||||
# misalignment we use async_track_time_interval here
|
||||
return async_track_time_interval(hass, action, timedelta(seconds=1))
|
||||
|
||||
job = HassJob(action)
|
||||
matching_seconds = dt_util.parse_time_expression(second, 0, 59)
|
||||
matching_minutes = dt_util.parse_time_expression(minute, 0, 59)
|
||||
matching_hours = dt_util.parse_time_expression(hour, 0, 23)
|
||||
|
||||
def calculate_next(now: datetime) -> datetime:
|
||||
"""Calculate and set the next time the trigger should fire."""
|
||||
ts_now = now.astimezone(tz) if tz else now
|
||||
return dt_util.find_next_time_expression_time(
|
||||
ts_now, matching_seconds, matching_minutes, matching_hours
|
||||
)
|
||||
|
||||
time_listener: CALLBACK_TYPE | None = None
|
||||
|
||||
@callback
|
||||
def pattern_time_change_listener(_: datetime) -> None:
|
||||
"""Listen for matching time_changed events."""
|
||||
nonlocal time_listener
|
||||
|
||||
now = time_tracker_utcnow()
|
||||
hass.async_run_hass_job(job, now.astimezone(tz) if tz else now)
|
||||
|
||||
time_listener = async_track_point_in_utc_time(
|
||||
hass,
|
||||
pattern_time_change_listener,
|
||||
calculate_next(now + timedelta(seconds=1)),
|
||||
)
|
||||
|
||||
time_listener = async_track_point_in_utc_time(
|
||||
hass, pattern_time_change_listener, calculate_next(dt_util.utcnow())
|
||||
)
|
||||
|
||||
@callback
|
||||
def unsub_pattern_time_change_listener() -> None:
|
||||
"""Cancel the time listener."""
|
||||
assert time_listener is not None
|
||||
time_listener()
|
||||
|
||||
return unsub_pattern_time_change_listener
|
||||
|
||||
|
||||
@callback
|
||||
@bind_hass
|
||||
def async_track_time_change_in_tz(
|
||||
hass: HomeAssistant,
|
||||
action: None,
|
||||
# action: Callable[[datetime], Awaitable[None] | None],
|
||||
hour: Optional[Any] = None,
|
||||
minute: Optional[Any] = None,
|
||||
second: Optional[Any] = None,
|
||||
tz: Optional[Any] = None,
|
||||
) -> CALLBACK_TYPE:
|
||||
"""Add a listener that will fire if UTC time matches a pattern."""
|
||||
return async_track_utc_time_change(hass, action, hour, minute, second, tz)
|
||||
Reference in New Issue
Block a user