Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
124 lines (97 sloc) 3.57 KB
"""Helpers for sun events."""
import datetime
from typing import TYPE_CHECKING, Optional, Union
from homeassistant.const import SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET
from homeassistant.core import callback
from homeassistant.loader import bind_hass
from homeassistant.util import dt as dt_util
from .typing import HomeAssistantType
if TYPE_CHECKING:
import astral # pylint: disable=unused-import
DATA_LOCATION_CACHE = "astral_location_cache"
@callback
@bind_hass
def get_astral_location(hass: HomeAssistantType) -> "astral.Location":
"""Get an astral location for the current Home Assistant configuration."""
from astral import Location
latitude = hass.config.latitude
longitude = hass.config.longitude
timezone = str(hass.config.time_zone)
elevation = hass.config.elevation
info = ("", "", latitude, longitude, timezone, elevation)
# Cache astral locations so they aren't recreated with the same args
if DATA_LOCATION_CACHE not in hass.data:
hass.data[DATA_LOCATION_CACHE] = {}
if info not in hass.data[DATA_LOCATION_CACHE]:
hass.data[DATA_LOCATION_CACHE][info] = Location(info)
return hass.data[DATA_LOCATION_CACHE][info]
@callback
@bind_hass
def get_astral_event_next(
hass: HomeAssistantType,
event: str,
utc_point_in_time: Optional[datetime.datetime] = None,
offset: Optional[datetime.timedelta] = None,
) -> datetime.datetime:
"""Calculate the next specified solar event."""
location = get_astral_location(hass)
return get_location_astral_event_next(location, event, utc_point_in_time, offset)
@callback
def get_location_astral_event_next(
location: "astral.Location",
event: str,
utc_point_in_time: Optional[datetime.datetime] = None,
offset: Optional[datetime.timedelta] = None,
) -> datetime.datetime:
"""Calculate the next specified solar event."""
from astral import AstralError
if offset is None:
offset = datetime.timedelta()
if utc_point_in_time is None:
utc_point_in_time = dt_util.utcnow()
mod = -1
while True:
try:
next_dt: datetime.datetime = (
getattr(location, event)(
dt_util.as_local(utc_point_in_time).date()
+ datetime.timedelta(days=mod),
local=False,
)
+ offset
)
if next_dt > utc_point_in_time:
return next_dt
except AstralError:
pass
mod += 1
@callback
@bind_hass
def get_astral_event_date(
hass: HomeAssistantType,
event: str,
date: Union[datetime.date, datetime.datetime, None] = None,
) -> Optional[datetime.datetime]:
"""Calculate the astral event time for the specified date."""
from astral import AstralError
location = get_astral_location(hass)
if date is None:
date = dt_util.now().date()
if isinstance(date, datetime.datetime):
date = dt_util.as_local(date).date()
try:
return getattr(location, event)(date, local=False) # type: ignore
except AstralError:
# Event never occurs for specified date.
return None
@callback
@bind_hass
def is_up(
hass: HomeAssistantType, utc_point_in_time: Optional[datetime.datetime] = None
) -> bool:
"""Calculate if the sun is currently up."""
if utc_point_in_time is None:
utc_point_in_time = dt_util.utcnow()
next_sunrise = get_astral_event_next(hass, SUN_EVENT_SUNRISE, utc_point_in_time)
next_sunset = get_astral_event_next(hass, SUN_EVENT_SUNSET, utc_point_in_time)
return next_sunrise > next_sunset
You can’t perform that action at this time.