1 """Helpers for sun events."""
3 from __future__
import annotations
5 from collections.abc
import Callable
7 from typing
import TYPE_CHECKING, Any, cast
17 import astral.location
19 DATA_LOCATION_CACHE: HassKey[
20 dict[tuple[str, str, str, float, float], astral.location.Location]
21 ] =
HassKey(
"astral_location_cache")
23 ELEVATION_AGNOSTIC_EVENTS = (
"noon",
"midnight")
25 type _AstralSunEventCallable = Callable[..., datetime.datetime]
32 ) -> tuple[astral.location.Location, astral.Elevation]:
33 """Get an astral location for the current Home Assistant configuration."""
34 from astral
import LocationInfo
35 from astral.location
import Location
37 latitude = hass.config.latitude
38 longitude = hass.config.longitude
39 timezone =
str(hass.config.time_zone)
40 elevation = hass.config.elevation
41 info = (
"",
"", timezone, latitude, longitude)
44 if DATA_LOCATION_CACHE
not in hass.data:
45 hass.data[DATA_LOCATION_CACHE] = {}
47 if info
not in hass.data[DATA_LOCATION_CACHE]:
48 hass.data[DATA_LOCATION_CACHE][info] = Location(LocationInfo(*info))
50 return hass.data[DATA_LOCATION_CACHE][info], elevation
58 utc_point_in_time: datetime.datetime |
None =
None,
59 offset: datetime.timedelta |
None =
None,
60 ) -> datetime.datetime:
61 """Calculate the next specified solar event."""
64 location, elevation, event, utc_point_in_time, offset
70 location: astral.location.Location,
71 elevation: astral.Elevation,
73 utc_point_in_time: datetime.datetime |
None =
None,
74 offset: datetime.timedelta |
None =
None,
75 ) -> datetime.datetime:
76 """Calculate the next specified solar event."""
79 offset = datetime.timedelta()
81 if utc_point_in_time
is None:
82 utc_point_in_time = dt_util.utcnow()
84 kwargs: dict[str, Any] = {
"local":
False}
85 if event
not in ELEVATION_AGNOSTIC_EVENTS:
86 kwargs[
"observer_elevation"] = elevation
93 cast(_AstralSunEventCallable, getattr(location, event))(
94 dt_util.as_local(utc_point_in_time).
date()
95 + datetime.timedelta(days=mod),
100 if next_dt > utc_point_in_time:
102 except ValueError
as err:
107 f
"Unable to find event after one year, initial ValueError: {first_err}"
116 date: datetime.date | datetime.datetime |
None =
None,
117 ) -> datetime.datetime |
None:
118 """Calculate the astral event time for the specified date."""
122 date = dt_util.now().
date()
124 if isinstance(date, datetime.datetime):
125 date = dt_util.as_local(date).
date()
127 kwargs: dict[str, Any] = {
"local":
False}
128 if event
not in ELEVATION_AGNOSTIC_EVENTS:
129 kwargs[
"observer_elevation"] = elevation
132 return cast(_AstralSunEventCallable, getattr(location, event))(date, **kwargs)
141 hass: HomeAssistant, utc_point_in_time: datetime.datetime |
None =
None
143 """Calculate if the sun is currently up."""
144 if utc_point_in_time
is None:
145 utc_point_in_time = dt_util.utcnow()
150 return next_sunrise > next_sunset
datetime.datetime|None get_astral_event_date(HomeAssistant hass, str event, datetime.date|datetime.datetime|None date=None)
tuple[astral.location.Location, astral.Elevation] get_astral_location(HomeAssistant hass)
bool is_up(HomeAssistant hass, datetime.datetime|None utc_point_in_time=None)
datetime.datetime get_astral_event_next(HomeAssistant hass, str event, datetime.datetime|None utc_point_in_time=None, datetime.timedelta|None offset=None)
datetime.datetime get_location_astral_event_next(astral.location.Location location, astral.Elevation elevation, str event, datetime.datetime|None utc_point_in_time=None, datetime.timedelta|None offset=None)