def get_loc_elev(event=None): """Get HA Location object & elevation.""" try: loc, elev = get_astral_location(hass) except TypeError: loc = get_astral_location(hass) elev = None hass.data["illuminance"] = loc, elev
async def setup_auto_fetch(self): try: _LOGGER.debug( "registering API auto fetching hourly between sun up and sun set" ) location, elevation = get_astral_location(self._hass) next_setting = get_location_astral_event_next( location, elevation, SUN_EVENT_SUNSET, dt_util.utcnow()) + timedelta(hours=1) self._finishhour = next_setting.astimezone( ).hour # already one hour ahead self._debugData["auto_fetch_end_hour"] = self._finishhour self._auto_fetch_tracker = async_track_utc_time_change( self._hass, self.update_forecast, minute=0, second=0, local=True) self._debugData["auto_fetch_timer_object"] = ( self._auto_fetch_tracker is not None) _LOGGER.debug("created auto forecast fetch hourly timer") _LOGGER.debug( "Remember that even though its every hour, the api will only connect between the hours %s and %s and at midnight", self._starthour, self._finishhour) except Exception: _LOGGER.error("setup_auto_fetch: %s", traceback.format_exc())
def sunrise_call_action(now=None): """Call action with right context.""" next_setting = get_location_astral_event_next( get_astral_location(self._hass), SUN_EVENT_SUNSET, dt_util.utcnow()) + timedelta(hours=1) _LOGGER.info( f"Good Morning! Time to prepare the day until the sun will set at {next_setting - timedelta(hours=1)}" ) remaining_api_calls = self.get_remaining_API_count() delay = (next_setting - dt_util.utcnow()) / (remaining_api_calls - 1) _LOGGER.info( f"During the day, there will be {remaining_api_calls} updates delayed by {delay} each" ) # Schedule updates over the day (starting on 0 to process early morning update) for i in range(0, remaining_api_calls): exec_delay = delay.total_seconds() * i exec_time = dt_util.utcnow() + timedelta(seconds=exec_delay) _LOGGER.info( f"History update scheduled update at {exec_time.isoformat()}" ) async_call_later(self._hass, exec_delay, self.update_history)
def update_location(_event): location, elevation = get_astral_location(self.hass) if location == self.location: return self.location = location self.elevation = elevation self.update_events()
def async_update(self): """Fetch new state data for the sensor. This is the only method that should fetch new data for Home Assistant. """ use_transition = self._use_transition transition_elevation = self._transition_elevation location = get_astral_location(self.hass) # Using local states for async method. Must sync before return. state = PHASE_UNKNOWN elevation = location.solar_elevation() _LOGGER.debug("Current elevation: %r" % elevation) attributes = self._attributes or {} attributes[ATTR_ELEVATION] = elevation state = get_astral_phase(location, use_transition=use_transition, transition_elevation=transition_elevation) self._state = state self._icon = PHASE_ICONS[state] self._attributes = attributes _LOGGER.debug("Updated astral_phase: %s, %f" % (state, elevation))
def _get_sun_events(self, date): def _replace_time(date, key): other_date = getattr(self, f"_{key}_time") return date.replace( hour=other_date.hour, minute=other_date.minute, second=other_date.second, microsecond=other_date.microsecond, ) location = get_astral_location(self.hass) sunrise = ( location.sunrise(date, local=False) if self._sunrise_time is None else _replace_time(date, "sunrise") ) + self._sunrise_offset sunset = ( location.sunset(date, local=False) if self._sunset_time is None else _replace_time(date, "sunset") ) + self._sunset_offset if self._sunrise_time is None and self._sunset_time is None: solar_noon = location.solar_noon(date, local=False) solar_midnight = location.solar_midnight(date, local=False) else: solar_noon = sunrise + (sunset - sunrise) / 2 solar_midnight = sunset + ((sunrise + timedelta(days=1)) - sunset) / 2 return { SUN_EVENT_SUNRISE: sunrise.timestamp(), SUN_EVENT_SUNSET: sunset.timestamp(), SUN_EVENT_NOON: solar_noon.timestamp(), SUN_EVENT_MIDNIGHT: solar_midnight.timestamp(), }
def async_init_astral_loc(hass): """Initialize astral Location.""" global _LOC, _HASS if not _LOC: _HASS = hass _LOC = get_astral_location(hass) hass.bus.async_listen(EVENT_CORE_CONFIG_UPDATE, _update_location)
async def async_setup(hass, config): """Track the state of the sun.""" if config.get(CONF_ELEVATION) is not None: _LOGGER.warning( "Elevation is now configured in home assistant core. " "See https://home-assistant.io/docs/configuration/basic/") Sun(hass, get_astral_location(hass)) return True
def update_location(_event): location, elevation = get_astral_location(self.hass) if location == self.location: return self.location = location self.elevation = elevation if self._update_events_listener: self._update_events_listener() self.update_events()
def _get_sun_events(self, date): if self._manual_sunrise is not None and self._manual_sunset is not None: sunrise = self._replace_time(date, "sunrise") sunset = self._replace_time(date, "sunset") solar_noon = sunrise + (sunset - sunrise) / 2 solar_midnight = sunset + ( (sunrise + timedelta(days=1)) - sunset) / 2 else: _loc = get_astral_location(self.hass) if isinstance(_loc, tuple): # Astral v2.2 location, _ = _loc else: # Astral v1 location = _loc location.name = "name" location.region = "region" location.latitude = self._latitude location.longitude = self._longitude location.elevation = self._elevation if self._manual_sunrise is not None: sunrise = self._replace_time(date, "sunrise") else: sunrise = location.sunrise(date) if self._manual_sunset is not None: sunset = self._replace_time(date, "sunset") else: sunset = location.sunset(date) try: solar_noon = location.noon(date) except AttributeError: solar_noon = location.solar_noon(date) try: solar_midnight = location.midnight(date) except AttributeError: solar_midnight = location.solar_midnight(date) if self._sunrise_offset is not None: sunrise = sunrise + self._sunrise_offset if self._sunset_offset is not None: sunset = sunset + self._sunset_offset datetimes = { SUN_EVENT_SUNRISE: sunrise, SUN_EVENT_SUNSET: sunset, SUN_EVENT_NOON: solar_noon, SUN_EVENT_MIDNIGHT: solar_midnight, } return { k: dt.astimezone(dt_util.UTC).timestamp() for k, dt in datetimes.items() }
def async_setup(hass, config): """Track the state of the sun.""" if config.get(CONF_ELEVATION) is not None: _LOGGER.warning( "Elevation is now configured in home assistant core. " "See https://home-assistant.io/docs/configuration/basic/") sun = Sun(hass, get_astral_location(hass), config[DOMAIN]) sun.point_in_time_listener(dt_util.utcnow()) return True
async def async_setup(hass, config): """Track the state of the sun.""" if config.get(CONF_ELEVATION) is not None: _LOGGER.warning( "Elevation is now configured in home assistant core. " "See https://home-assistant.io/docs/configuration/basic/") sun = Sun(hass, get_astral_location(hass)) sun.point_in_time_listener(dt_util.utcnow()) return True
def update_location(event): self.location = get_astral_location(self.hass) self.update_events(dt_util.utcnow())
def parse_date_time(cls, date_time_str, day_offset, now): """Parse a date time string, returning datetime.""" year = now.year month = now.month day = now.day dt_str = date_time_str.strip().lower() # # parse the date # skip = True match0 = re.split(r"^0*(\d+)[-/]0*(\d+)(?:[-/]0*(\d+))?", dt_str) match1 = re.split(r"^(\w+).*", dt_str) if len(match0) == 5: if match0[3] is None: month, day = int(match0[1]), int(match0[2]) else: year, month, day = int(match0[1]), int(match0[2]), int(match0[3]) day_offset = 0 # explicit date means no offset elif len(match1) == 3: if match1[1] in cls.dow2int: dow = cls.dow2int[match1[1]] if dow >= (now.isoweekday() % 7): day_offset = dow - (now.isoweekday() % 7) else: day_offset = 7 + dow - (now.isoweekday() % 7) elif match1[1] == "today": day_offset = 0 elif match1[1] == "tomorrow": day_offset = 1 else: skip = False else: skip = False if day_offset != 0: now = dt.datetime(year, month, day) + dt.timedelta(days=day_offset) year = now.year month = now.month day = now.day else: now = dt.datetime(year, month, day) if skip: i = dt_str.find(" ") if i >= 0: dt_str = dt_str[i + 1 :].strip() else: return now # # parse the time # skip = True match0 = re.split(r"0*(\d+):0*(\d+)(?::0*(\d*\.?\d+(?:[eE][-+]?\d+)?))?", dt_str) if len(match0) == 5: if match0[3] is not None: hour, mins, sec = int(match0[1]), int(match0[2]), float(match0[3]) else: hour, mins, sec = int(match0[1]), int(match0[2]), 0 elif dt_str.startswith("sunrise") or dt_str.startswith("sunset"): location = sun.get_astral_location(cls.hass) try: if dt_str.startswith("sunrise"): time_sun = location.sunrise(dt.date(year, month, day)) else: time_sun = location.sunset(dt.date(year, month, day)) except Exception: _LOGGER.warning("'%s' not defined at this latitude", dt_str) # return something in the past so it is ignored return now - dt.timedelta(days=100) now += time_sun.date() - now.date() hour, mins, sec = time_sun.hour, time_sun.minute, time_sun.second elif dt_str.startswith("noon"): hour, mins, sec = 12, 0, 0 elif dt_str.startswith("midnight"): hour, mins, sec = 0, 0, 0 else: hour, mins, sec = 0, 0, 0 skip = False now += dt.timedelta(seconds=sec + 60 * (mins + 60 * hour)) if skip: i = dt_str.find(" ") if i >= 0: dt_str = dt_str[i + 1 :].strip() else: return now # # parse the offset # if len(dt_str) > 0 and (dt_str[0] == "+" or dt_str[0] == "-"): now = now + dt.timedelta(seconds=parse_time_offset(dt_str)) return now
def async_update_location(event=None): self._location = get_astral_location(self.hass) self.async_schedule_update_ha_state(True)
def __init__( self, hass, config_entry: ConfigEntry, turn_on_off_listener: TurnOnOffListener, sleep_mode_switch: AdaptiveSleepModeSwitch, ): """Initialize the Adaptive Lighting switch.""" self.hass = hass self.turn_on_off_listener = turn_on_off_listener self.sleep_mode_switch = sleep_mode_switch data = validate(config_entry) self._name = data[CONF_NAME] self._lights = data[CONF_LIGHTS] self._adapt_brightness = data[CONF_ADAPT_BRIGHTNESS] self._adapt_color_temp = data[CONF_ADAPT_COLOR_TEMP] self._adapt_rgb_color = data[CONF_ADAPT_RGB_COLOR] self._detect_non_ha_changes = data[CONF_DETECT_NON_HA_CHANGES] self._initial_transition = data[CONF_INITIAL_TRANSITION] self._interval = data[CONF_INTERVAL] self._only_once = data[CONF_ONLY_ONCE] self._prefer_rgb_color = data[CONF_PREFER_RGB_COLOR] self._take_over_control = data[CONF_TAKE_OVER_CONTROL] self._transition = min(data[CONF_TRANSITION], self._interval.total_seconds() // 2) self._sun_light_settings = SunLightSettings( name=self._name, astral_location=get_astral_location(self.hass), max_brightness=data[CONF_MAX_BRIGHTNESS], max_color_temp=data[CONF_MAX_COLOR_TEMP], min_brightness=data[CONF_MIN_BRIGHTNESS], min_color_temp=data[CONF_MIN_COLOR_TEMP], sleep_brightness=data[CONF_SLEEP_BRIGHTNESS], sleep_color_temp=data[CONF_SLEEP_COLOR_TEMP], sunrise_offset=data[CONF_SUNRISE_OFFSET], sunrise_time=data[CONF_SUNRISE_TIME], sunset_offset=data[CONF_SUNSET_OFFSET], sunset_time=data[CONF_SUNSET_TIME], time_zone=self.hass.config.time_zone, ) # Set other attributes self._icon = ICON self._state = None # Tracks 'off' → 'on' state changes self._on_to_off_event: Dict[str, Event] = {} # Tracks 'on' → 'off' state changes self._off_to_on_event: Dict[str, Event] = {} # Locks that prevent light adjusting when waiting for a light to 'turn_off' self._locks: Dict[str, asyncio.Lock] = {} # To count the number of `Context` instances self._context_cnt: int = 0 # Set in self._update_attrs_and_maybe_adapt_lights self._settings: Dict[str, Any] = {} # Set and unset tracker in async_turn_on and async_turn_off self.remove_listeners = [] _LOGGER.debug( "%s: Setting up with '%s'," " config_entry.data: '%s'," " config_entry.options: '%s', converted to '%s'.", self._name, self._lights, config_entry.data, config_entry.options, data, )
def update_location(_event): location = get_astral_location(self.hass) if location == self.location: return self.location = location self.update_events(dt_util.utcnow())
def _update_location(event): global _LOC _LOC = get_astral_location(_HASS) dispatcher_send(_HASS, SIG_LOC_UPDATED)