def sun( opp: OpenPeerPower, before: Optional[str] = None, after: Optional[str] = None, before_offset: Optional[timedelta] = None, after_offset: Optional[timedelta] = None, ) -> bool: """Test if current time matches sun requirements.""" utcnow = dt_util.utcnow() today = dt_util.as_local(utcnow).date() before_offset = before_offset or timedelta(0) after_offset = after_offset or timedelta(0) sunrise_today = get_astral_event_date(opp, SUN_EVENT_SUNRISE, today) sunset_today = get_astral_event_date(opp, SUN_EVENT_SUNSET, today) sunrise = sunrise_today sunset = sunset_today if today > dt_util.as_local(cast( datetime, sunrise_today)).date() and SUN_EVENT_SUNRISE in (before, after): tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() sunrise_tomorrow = get_astral_event_date(opp, SUN_EVENT_SUNRISE, tomorrow) sunrise = sunrise_tomorrow if today > dt_util.as_local(cast( datetime, sunset_today)).date() and SUN_EVENT_SUNSET in (before, after): tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() sunset_tomorrow = get_astral_event_date(opp, SUN_EVENT_SUNSET, tomorrow) sunset = sunset_tomorrow if sunrise is None and SUN_EVENT_SUNRISE in (before, after): # There is no sunrise today return False if sunset is None and SUN_EVENT_SUNSET in (before, after): # There is no sunset today return False if before == SUN_EVENT_SUNRISE and utcnow > cast(datetime, sunrise) + before_offset: return False if before == SUN_EVENT_SUNSET and utcnow > cast(datetime, sunset) + before_offset: return False if after == SUN_EVENT_SUNRISE and utcnow < cast(datetime, sunrise) + after_offset: return False if after == SUN_EVENT_SUNSET and utcnow < cast(datetime, sunset) + after_offset: return False return True
def find_stop_time(self, now): """Return dusk or stop_time if given.""" if self._stop_time: dusk = now.replace( hour=self._stop_time.hour, minute=self._stop_time.minute, second=0 ) else: dusk = get_astral_event_date(self.opp, "dusk", now.date()) return dusk
def find_start_time(self, now): """Return sunrise or start_time if given.""" if self._start_time: sunrise = now.replace( hour=self._start_time.hour, minute=self._start_time.minute, second=0 ) else: sunrise = get_astral_event_date(self.opp, SUN_EVENT_SUNRISE, now.date()) return sunrise
def _calculate_boudary_time(self): """Calculate internal absolute time boundaries.""" nowutc = dt_util.utcnow() # If after value is a sun event instead of absolute time if is_sun_event(self._after): # Calculate the today's event utc time or # if not available take next after_event_date = get_astral_event_date( self.opp, self._after, nowutc) or get_astral_event_next( self.opp, self._after, nowutc) else: # Convert local time provided to UTC today # datetime.combine(date, time, tzinfo) is not supported # in python 3.5. The self._after is provided # with opp configured TZ not system wide after_event_date = self._naive_time_to_utc_datetime(self._after) self._time_after = after_event_date # If before value is a sun event instead of absolute time if is_sun_event(self._before): # Calculate the today's event utc time or if not available take # next before_event_date = get_astral_event_date( self.opp, self._before, nowutc) or get_astral_event_next( self.opp, self._before, nowutc) # Before is earlier than after if before_event_date < after_event_date: # Take next day for before before_event_date = get_astral_event_next( self.opp, self._before, after_event_date) else: # Convert local time provided to UTC today, see above before_event_date = self._naive_time_to_utc_datetime(self._before) # It is safe to add timedelta days=1 to UTC as there is no DST if before_event_date < after_event_date + self._after_offset: before_event_date += timedelta(days=1) self._time_before = before_event_date # Add offset to utc boundaries according to the configuration self._time_after += self._after_offset self._time_before += self._before_offset
def test_date_events(opp): """Test retrieving next sun events.""" utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC) from astral import LocationInfo import astral.sun utc_today = utc_now.date() location = LocationInfo(latitude=opp.config.latitude, longitude=opp.config.longitude) dawn = astral.sun.dawn(location.observer, utc_today) dusk = astral.sun.dusk(location.observer, utc_today) midnight = astral.sun.midnight(location.observer, utc_today) noon = astral.sun.noon(location.observer, utc_today) sunrise = astral.sun.sunrise(location.observer, utc_today) sunset = astral.sun.sunset(location.observer, utc_today) assert dawn == sun.get_astral_event_date(opp, "dawn", utc_today) assert dusk == sun.get_astral_event_date(opp, "dusk", utc_today) assert midnight == sun.get_astral_event_date(opp, "midnight", utc_today) assert noon == sun.get_astral_event_date(opp, "noon", utc_today) assert sunrise == sun.get_astral_event_date(opp, SUN_EVENT_SUNRISE, utc_today) assert sunset == sun.get_astral_event_date(opp, SUN_EVENT_SUNSET, utc_today)
def test_norway_in_june(opp): """Test location in Norway where the sun doesn't set in summer.""" opp.config.latitude = 69.6 opp.config.longitude = 18.8 june = datetime(2016, 6, 1, tzinfo=dt_util.UTC) print( sun.get_astral_event_date(opp, SUN_EVENT_SUNRISE, datetime(2017, 7, 25))) print( sun.get_astral_event_date(opp, SUN_EVENT_SUNSET, datetime(2017, 7, 25))) print( sun.get_astral_event_date(opp, SUN_EVENT_SUNRISE, datetime(2017, 7, 26))) print( sun.get_astral_event_date(opp, SUN_EVENT_SUNSET, datetime(2017, 7, 26))) assert sun.get_astral_event_next(opp, SUN_EVENT_SUNRISE, june) == datetime(2016, 7, 24, 22, 59, 45, 689645, tzinfo=dt_util.UTC) assert sun.get_astral_event_next(opp, SUN_EVENT_SUNSET, june) == datetime(2016, 7, 25, 22, 17, 13, 503932, tzinfo=dt_util.UTC) assert sun.get_astral_event_date(opp, SUN_EVENT_SUNRISE, june) is None assert sun.get_astral_event_date(opp, SUN_EVENT_SUNSET, june) is None
async def async_flux_update(self, utcnow=None): """Update all the lights using flux.""" if utcnow is None: utcnow = dt_utcnow() now = as_local(utcnow) sunset = get_astral_event_date(self.opp, SUN_EVENT_SUNSET, now.date()) start_time = self.find_start_time(now) stop_time = self.find_stop_time(now) if stop_time <= start_time: # stop_time does not happen in the same day as start_time if start_time < now: # stop time is tomorrow stop_time += datetime.timedelta(days=1) elif now < start_time: # stop_time was yesterday since the new start_time is not reached stop_time -= datetime.timedelta(days=1) if start_time < now < sunset: # Daytime time_state = "day" temp_range = abs(self._start_colortemp - self._sunset_colortemp) day_length = int(sunset.timestamp() - start_time.timestamp()) seconds_from_start = int(now.timestamp() - start_time.timestamp()) percentage_complete = seconds_from_start / day_length temp_offset = temp_range * percentage_complete if self._start_colortemp > self._sunset_colortemp: temp = self._start_colortemp - temp_offset else: temp = self._start_colortemp + temp_offset else: # Night time time_state = "night" if now < stop_time: if stop_time < start_time and stop_time.day == sunset.day: # we need to use yesterday's sunset time sunset_time = sunset - datetime.timedelta(days=1) else: sunset_time = sunset night_length = int(stop_time.timestamp() - sunset_time.timestamp()) seconds_from_sunset = int(now.timestamp() - sunset_time.timestamp()) percentage_complete = seconds_from_sunset / night_length else: percentage_complete = 1 temp_range = abs(self._sunset_colortemp - self._stop_colortemp) temp_offset = temp_range * percentage_complete if self._sunset_colortemp > self._stop_colortemp: temp = self._sunset_colortemp - temp_offset else: temp = self._sunset_colortemp + temp_offset rgb = color_temperature_to_rgb(temp) x_val, y_val, b_val = color_RGB_to_xy_brightness(*rgb) brightness = self._brightness if self._brightness else b_val if self._disable_brightness_adjust: brightness = None if self._mode == MODE_XY: await async_set_lights_xy( self.opp, self._lights, x_val, y_val, brightness, self._transition ) _LOGGER.debug( "Lights updated to x:%s y:%s brightness:%s, %s%% " "of %s cycle complete at %s", x_val, y_val, brightness, round(percentage_complete * 100), time_state, now, ) elif self._mode == MODE_RGB: await async_set_lights_rgb(self.opp, self._lights, rgb, self._transition) _LOGGER.debug( "Lights updated to rgb:%s, %s%% of %s cycle complete at %s", rgb, round(percentage_complete * 100), time_state, now, ) else: # Convert to mired and clamp to allowed values mired = color_temperature_kelvin_to_mired(temp) await async_set_lights_temp( self.opp, self._lights, mired, brightness, self._transition ) _LOGGER.debug( "Lights updated to mired:%s brightness:%s, %s%% " "of %s cycle complete at %s", mired, brightness, round(percentage_complete * 100), time_state, now, )
def sun( opp: OpenPeerPower, before: str | None = None, after: str | None = None, before_offset: timedelta | None = None, after_offset: timedelta | None = None, ) -> bool: """Test if current time matches sun requirements.""" utcnow = dt_util.utcnow() today = dt_util.as_local(utcnow).date() before_offset = before_offset or timedelta(0) after_offset = after_offset or timedelta(0) sunrise_today = get_astral_event_date(opp, SUN_EVENT_SUNRISE, today) sunset_today = get_astral_event_date(opp, SUN_EVENT_SUNSET, today) sunrise = sunrise_today sunset = sunset_today if today > dt_util.as_local( cast(datetime, sunrise_today) ).date() and SUN_EVENT_SUNRISE in (before, after): tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() sunrise_tomorrow = get_astral_event_date(opp, SUN_EVENT_SUNRISE, tomorrow) sunrise = sunrise_tomorrow if today > dt_util.as_local( cast(datetime, sunset_today) ).date() and SUN_EVENT_SUNSET in (before, after): tomorrow = dt_util.as_local(utcnow + timedelta(days=1)).date() sunset_tomorrow = get_astral_event_date(opp, SUN_EVENT_SUNSET, tomorrow) sunset = sunset_tomorrow if sunrise is None and SUN_EVENT_SUNRISE in (before, after): # There is no sunrise today condition_trace_set_result(False, message="no sunrise today") return False if sunset is None and SUN_EVENT_SUNSET in (before, after): # There is no sunset today condition_trace_set_result(False, message="no sunset today") return False if before == SUN_EVENT_SUNRISE: wanted_time_before = cast(datetime, sunrise) + before_offset condition_trace_update_result(wanted_time_before=wanted_time_before) if utcnow > wanted_time_before: return False if before == SUN_EVENT_SUNSET: wanted_time_before = cast(datetime, sunset) + before_offset condition_trace_update_result(wanted_time_before=wanted_time_before) if utcnow > wanted_time_before: return False if after == SUN_EVENT_SUNRISE: wanted_time_after = cast(datetime, sunrise) + after_offset condition_trace_update_result(wanted_time_after=wanted_time_after) if utcnow < wanted_time_after: return False if after == SUN_EVENT_SUNSET: wanted_time_after = cast(datetime, sunset) + after_offset condition_trace_update_result(wanted_time_after=wanted_time_after) if utcnow < wanted_time_after: return False return True