Quiet the chatty sun.sun (#23832)
* Split up method to allow caching event * Lower frequency of updates. * Code review patches. * Minor changes to test * Skip end of period at fixed multiple of delta. Improved documentation.pull/23877/head
parent
18149dcb8c
commit
9da74dda43
|
@ -6,10 +6,9 @@ from homeassistant.const import (
|
|||
CONF_ELEVATION, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET)
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.helpers.entity import Entity
|
||||
from homeassistant.helpers.event import (
|
||||
async_track_point_in_utc_time, async_track_utc_time_change)
|
||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||
from homeassistant.helpers.sun import (
|
||||
get_astral_location, get_astral_event_next)
|
||||
get_astral_location, get_location_astral_event_next)
|
||||
from homeassistant.util import dt as dt_util
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
@ -23,6 +22,7 @@ STATE_BELOW_HORIZON = 'below_horizon'
|
|||
|
||||
STATE_ATTR_AZIMUTH = 'azimuth'
|
||||
STATE_ATTR_ELEVATION = 'elevation'
|
||||
STATE_ATTR_RISING = 'rising'
|
||||
STATE_ATTR_NEXT_DAWN = 'next_dawn'
|
||||
STATE_ATTR_NEXT_DUSK = 'next_dusk'
|
||||
STATE_ATTR_NEXT_MIDNIGHT = 'next_midnight'
|
||||
|
@ -30,6 +30,39 @@ STATE_ATTR_NEXT_NOON = 'next_noon'
|
|||
STATE_ATTR_NEXT_RISING = 'next_rising'
|
||||
STATE_ATTR_NEXT_SETTING = 'next_setting'
|
||||
|
||||
# The algorithm used here is somewhat complicated. It aims to cut down
|
||||
# the number of sensor updates over the day. It's documented best in
|
||||
# the PR for the change, see the Discussion section of:
|
||||
# https://github.com/home-assistant/home-assistant/pull/23832
|
||||
|
||||
|
||||
# As documented in wikipedia: https://en.wikipedia.org/wiki/Twilight
|
||||
# sun is:
|
||||
# < -18° of horizon - all stars visible
|
||||
PHASE_NIGHT = 'night'
|
||||
# 18°-12° - some stars not visible
|
||||
PHASE_ASTRONOMICAL_TWILIGHT = 'astronomical_twilight'
|
||||
# 12°-6° - horizon visible
|
||||
PHASE_NAUTICAL_TWILIGHT = 'nautical_twilight'
|
||||
# 6°-0° - objects visible
|
||||
PHASE_TWILIGHT = 'twilight'
|
||||
# 0°-10° above horizon, sun low on horizon
|
||||
PHASE_SMALL_DAY = 'small_day'
|
||||
# > 10° above horizon
|
||||
PHASE_DAY = 'day'
|
||||
|
||||
# 4 mins is one degree of arc change of the sun on its circle.
|
||||
# During the night and the middle of the day we don't update
|
||||
# that much since it's not important.
|
||||
_PHASE_UPDATES = {
|
||||
PHASE_NIGHT: timedelta(minutes=4*5),
|
||||
PHASE_ASTRONOMICAL_TWILIGHT: timedelta(minutes=4*2),
|
||||
PHASE_NAUTICAL_TWILIGHT: timedelta(minutes=4*2),
|
||||
PHASE_TWILIGHT: timedelta(minutes=4),
|
||||
PHASE_SMALL_DAY: timedelta(minutes=2),
|
||||
PHASE_DAY: timedelta(minutes=4),
|
||||
}
|
||||
|
||||
|
||||
async def async_setup(hass, config):
|
||||
"""Track the state of the sun."""
|
||||
|
@ -37,10 +70,7 @@ async def async_setup(hass, config):
|
|||
_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())
|
||||
|
||||
Sun(hass, get_astral_location(hass))
|
||||
return True
|
||||
|
||||
|
||||
|
@ -57,8 +87,10 @@ class Sun(Entity):
|
|||
self.next_dawn = self.next_dusk = None
|
||||
self.next_midnight = self.next_noon = None
|
||||
self.solar_elevation = self.solar_azimuth = None
|
||||
self.rising = self.phase = None
|
||||
|
||||
async_track_utc_time_change(hass, self.timer_update, second=30)
|
||||
self._next_change = None
|
||||
self.update_events(dt_util.utcnow())
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
|
@ -83,57 +115,110 @@ class Sun(Entity):
|
|||
STATE_ATTR_NEXT_NOON: self.next_noon.isoformat(),
|
||||
STATE_ATTR_NEXT_RISING: self.next_rising.isoformat(),
|
||||
STATE_ATTR_NEXT_SETTING: self.next_setting.isoformat(),
|
||||
STATE_ATTR_ELEVATION: round(self.solar_elevation, 2),
|
||||
STATE_ATTR_AZIMUTH: round(self.solar_azimuth, 2)
|
||||
STATE_ATTR_ELEVATION: self.solar_elevation,
|
||||
STATE_ATTR_AZIMUTH: self.solar_azimuth,
|
||||
STATE_ATTR_RISING: self.rising,
|
||||
}
|
||||
|
||||
@property
|
||||
def next_change(self):
|
||||
"""Datetime when the next change to the state is."""
|
||||
return min(self.next_dawn, self.next_dusk, self.next_midnight,
|
||||
self.next_noon, self.next_rising, self.next_setting)
|
||||
def _check_event(self, utc_point_in_time, event, before):
|
||||
next_utc = get_location_astral_event_next(
|
||||
self.location, event, utc_point_in_time)
|
||||
if next_utc < self._next_change:
|
||||
self._next_change = next_utc
|
||||
self.phase = before
|
||||
return next_utc
|
||||
|
||||
@callback
|
||||
def update_as_of(self, utc_point_in_time):
|
||||
def update_events(self, utc_point_in_time):
|
||||
"""Update the attributes containing solar events."""
|
||||
self.next_dawn = get_astral_event_next(
|
||||
self.hass, 'dawn', utc_point_in_time)
|
||||
self.next_dusk = get_astral_event_next(
|
||||
self.hass, 'dusk', utc_point_in_time)
|
||||
self.next_midnight = get_astral_event_next(
|
||||
self.hass, 'solar_midnight', utc_point_in_time)
|
||||
self.next_noon = get_astral_event_next(
|
||||
self.hass, 'solar_noon', utc_point_in_time)
|
||||
self.next_rising = get_astral_event_next(
|
||||
self.hass, SUN_EVENT_SUNRISE, utc_point_in_time)
|
||||
self.next_setting = get_astral_event_next(
|
||||
self.hass, SUN_EVENT_SUNSET, utc_point_in_time)
|
||||
self._next_change = utc_point_in_time + timedelta(days=400)
|
||||
|
||||
# Work our way around the solar cycle, figure out the next
|
||||
# phase. Some of these are stored.
|
||||
self.location.solar_depression = 'astronomical'
|
||||
self._check_event(utc_point_in_time, 'dawn', PHASE_NIGHT)
|
||||
self.location.solar_depression = 'nautical'
|
||||
self._check_event(
|
||||
utc_point_in_time, 'dawn', PHASE_ASTRONOMICAL_TWILIGHT)
|
||||
self.location.solar_depression = 'civil'
|
||||
self.next_dawn = self._check_event(
|
||||
utc_point_in_time, 'dawn', PHASE_NAUTICAL_TWILIGHT)
|
||||
self.next_rising = self._check_event(
|
||||
utc_point_in_time, SUN_EVENT_SUNRISE, PHASE_TWILIGHT)
|
||||
self.location.solar_depression = -10
|
||||
self._check_event(utc_point_in_time, 'dawn', PHASE_SMALL_DAY)
|
||||
self.next_noon = self._check_event(
|
||||
utc_point_in_time, 'solar_noon', None)
|
||||
self._check_event(utc_point_in_time, 'dusk', PHASE_DAY)
|
||||
self.next_setting = self._check_event(
|
||||
utc_point_in_time, SUN_EVENT_SUNSET, PHASE_SMALL_DAY)
|
||||
self.location.solar_depression = 'civil'
|
||||
self.next_dusk = self._check_event(
|
||||
utc_point_in_time, 'dusk', PHASE_TWILIGHT)
|
||||
self.location.solar_depression = 'nautical'
|
||||
self._check_event(
|
||||
utc_point_in_time, 'dusk', PHASE_NAUTICAL_TWILIGHT)
|
||||
self.location.solar_depression = 'astronomical'
|
||||
self._check_event(
|
||||
utc_point_in_time, 'dusk', PHASE_ASTRONOMICAL_TWILIGHT)
|
||||
self.next_midnight = self._check_event(
|
||||
utc_point_in_time, 'solar_midnight', None)
|
||||
|
||||
# if the event was solar midday or midnight, phase will now
|
||||
# be None. Solar noon doesn't always happen when the sun is
|
||||
# even in the day at the poles, so we can't rely on it.
|
||||
# Need to calculate phase if next is noon or midnight
|
||||
if self.phase is None:
|
||||
elevation = self.location.solar_elevation(self._next_change)
|
||||
if elevation >= 10:
|
||||
self.phase = PHASE_DAY
|
||||
elif elevation >= 0:
|
||||
self.phase = PHASE_SMALL_DAY
|
||||
elif elevation >= -6:
|
||||
self.phase = PHASE_TWILIGHT
|
||||
elif elevation >= -12:
|
||||
self.phase = PHASE_NAUTICAL_TWILIGHT
|
||||
elif elevation >= -18:
|
||||
self.phase = PHASE_ASTRONOMICAL_TWILIGHT
|
||||
else:
|
||||
self.phase = PHASE_NIGHT
|
||||
|
||||
self.rising = self.next_noon < self.next_midnight
|
||||
|
||||
_LOGGER.debug(
|
||||
"sun phase_update@%s: phase=%s",
|
||||
utc_point_in_time.isoformat(),
|
||||
self.phase,
|
||||
)
|
||||
self.update_sun_position(utc_point_in_time)
|
||||
|
||||
# Set timer for the next solar event
|
||||
async_track_point_in_utc_time(
|
||||
self.hass, self.update_events,
|
||||
self._next_change)
|
||||
_LOGGER.debug("next time: %s", self._next_change.isoformat())
|
||||
|
||||
@callback
|
||||
def update_sun_position(self, utc_point_in_time):
|
||||
"""Calculate the position of the sun."""
|
||||
self.solar_azimuth = self.location.solar_azimuth(utc_point_in_time)
|
||||
self.solar_elevation = self.location.solar_elevation(utc_point_in_time)
|
||||
self.solar_azimuth = round(
|
||||
self.location.solar_azimuth(utc_point_in_time), 2)
|
||||
self.solar_elevation = round(
|
||||
self.location.solar_elevation(utc_point_in_time), 2)
|
||||
|
||||
@callback
|
||||
def point_in_time_listener(self, now):
|
||||
"""Run when the state of the sun has changed."""
|
||||
self.update_sun_position(now)
|
||||
self.update_as_of(now)
|
||||
_LOGGER.debug(
|
||||
"sun position_update@%s: elevation=%s azimuth=%s",
|
||||
utc_point_in_time.isoformat(),
|
||||
self.solar_elevation, self.solar_azimuth
|
||||
)
|
||||
self.async_write_ha_state()
|
||||
_LOGGER.debug("sun point_in_time_listener@%s: %s, %s",
|
||||
now, self.state, self.state_attributes)
|
||||
|
||||
# Schedule next update at next_change+1 second so sun state has changed
|
||||
# Next update as per the current phase
|
||||
delta = _PHASE_UPDATES[self.phase]
|
||||
# if the next update is within 1.25 of the next
|
||||
# position update just drop it
|
||||
if utc_point_in_time + delta*1.25 > self._next_change:
|
||||
return
|
||||
async_track_point_in_utc_time(
|
||||
self.hass, self.point_in_time_listener,
|
||||
self.next_change + timedelta(seconds=1))
|
||||
_LOGGER.debug("next time: %s", self.next_change + timedelta(seconds=1))
|
||||
|
||||
@callback
|
||||
def timer_update(self, time):
|
||||
"""Needed to update solar elevation and azimuth."""
|
||||
self.update_sun_position(time)
|
||||
self.async_write_ha_state()
|
||||
_LOGGER.debug("sun timer_update@%s: %s, %s",
|
||||
time, self.state, self.state_attributes)
|
||||
self.hass, self.update_sun_position,
|
||||
utc_point_in_time + delta)
|
||||
|
|
|
@ -43,9 +43,18 @@ def get_astral_event_next(
|
|||
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
|
||||
|
||||
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()
|
||||
|
|
|
@ -1,153 +1,177 @@
|
|||
"""The tests for the Sun component."""
|
||||
# pylint: disable=protected-access
|
||||
import unittest
|
||||
from datetime import datetime, timedelta
|
||||
from unittest.mock import patch
|
||||
from datetime import timedelta, datetime
|
||||
|
||||
from homeassistant.setup import setup_component
|
||||
from pytest import mark
|
||||
|
||||
import homeassistant.components.sun as sun
|
||||
import homeassistant.core as ha
|
||||
import homeassistant.util.dt as dt_util
|
||||
import homeassistant.components.sun as sun
|
||||
|
||||
from tests.common import get_test_home_assistant
|
||||
from homeassistant.const import EVENT_STATE_CHANGED
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
|
||||
# pylint: disable=invalid-name
|
||||
class TestSun(unittest.TestCase):
|
||||
"""Test the sun module."""
|
||||
async def test_setting_rising(hass):
|
||||
"""Test retrieving sun setting and rising."""
|
||||
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=utc_now):
|
||||
await async_setup_component(hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
|
||||
def setUp(self):
|
||||
"""Set up things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
await hass.async_block_till_done()
|
||||
state = hass.states.get(sun.ENTITY_ID)
|
||||
|
||||
def tearDown(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
from astral import Astral
|
||||
|
||||
def test_setting_rising(self):
|
||||
"""Test retrieving sun setting and rising."""
|
||||
utc_now = datetime(2016, 11, 1, 8, 0, 0, tzinfo=dt_util.UTC)
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=utc_now):
|
||||
setup_component(self.hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
astral = Astral()
|
||||
utc_today = utc_now.date()
|
||||
|
||||
self.hass.block_till_done()
|
||||
state = self.hass.states.get(sun.ENTITY_ID)
|
||||
latitude = hass.config.latitude
|
||||
longitude = hass.config.longitude
|
||||
|
||||
from astral import Astral
|
||||
mod = -1
|
||||
while True:
|
||||
next_dawn = (astral.dawn_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_dawn > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
astral = Astral()
|
||||
utc_today = utc_now.date()
|
||||
mod = -1
|
||||
while True:
|
||||
next_dusk = (astral.dusk_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_dusk > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
latitude = self.hass.config.latitude
|
||||
longitude = self.hass.config.longitude
|
||||
mod = -1
|
||||
while True:
|
||||
next_midnight = (astral.solar_midnight_utc(
|
||||
utc_today + timedelta(days=mod), longitude))
|
||||
if next_midnight > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_dawn = (astral.dawn_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_dawn > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
mod = -1
|
||||
while True:
|
||||
next_noon = (astral.solar_noon_utc(
|
||||
utc_today + timedelta(days=mod), longitude))
|
||||
if next_noon > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_dusk = (astral.dusk_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_dusk > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
mod = -1
|
||||
while True:
|
||||
next_rising = (astral.sunrise_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_rising > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_midnight = (astral.solar_midnight_utc(
|
||||
utc_today + timedelta(days=mod), longitude))
|
||||
if next_midnight > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
mod = -1
|
||||
while True:
|
||||
next_setting = (astral.sunset_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_setting > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_noon = (astral.solar_noon_utc(
|
||||
utc_today + timedelta(days=mod), longitude))
|
||||
if next_noon > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
assert next_dawn == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_DAWN])
|
||||
assert next_dusk == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_DUSK])
|
||||
assert next_midnight == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_MIDNIGHT])
|
||||
assert next_noon == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_NOON])
|
||||
assert next_rising == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_RISING])
|
||||
assert next_setting == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_SETTING])
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_rising = (astral.sunrise_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_rising > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
|
||||
mod = -1
|
||||
while True:
|
||||
next_setting = (astral.sunset_utc(
|
||||
utc_today + timedelta(days=mod), latitude, longitude))
|
||||
if next_setting > utc_now:
|
||||
break
|
||||
mod += 1
|
||||
async def test_state_change(hass):
|
||||
"""Test if the state changes at next setting/rising."""
|
||||
now = datetime(2016, 6, 1, 8, 0, 0, tzinfo=dt_util.UTC)
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=now):
|
||||
await async_setup_component(hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
|
||||
assert next_dawn == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_DAWN])
|
||||
assert next_dusk == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_DUSK])
|
||||
assert next_midnight == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_MIDNIGHT])
|
||||
assert next_noon == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_NOON])
|
||||
assert next_rising == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_RISING])
|
||||
assert next_setting == dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_SETTING])
|
||||
await hass.async_block_till_done()
|
||||
|
||||
def test_state_change(self):
|
||||
"""Test if the state changes at next setting/rising."""
|
||||
now = datetime(2016, 6, 1, 8, 0, 0, tzinfo=dt_util.UTC)
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=now):
|
||||
setup_component(self.hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
test_time = dt_util.parse_datetime(
|
||||
hass.states.get(sun.ENTITY_ID)
|
||||
.attributes[sun.STATE_ATTR_NEXT_RISING])
|
||||
assert test_time is not None
|
||||
|
||||
self.hass.block_till_done()
|
||||
assert sun.STATE_BELOW_HORIZON == \
|
||||
hass.states.get(sun.ENTITY_ID).state
|
||||
|
||||
test_time = dt_util.parse_datetime(
|
||||
self.hass.states.get(sun.ENTITY_ID)
|
||||
.attributes[sun.STATE_ATTR_NEXT_RISING])
|
||||
assert test_time is not None
|
||||
hass.bus.async_fire(
|
||||
ha.EVENT_TIME_CHANGED,
|
||||
{ha.ATTR_NOW: test_time + timedelta(seconds=5)})
|
||||
|
||||
assert sun.STATE_BELOW_HORIZON == \
|
||||
self.hass.states.get(sun.ENTITY_ID).state
|
||||
await hass.async_block_till_done()
|
||||
|
||||
self.hass.bus.fire(ha.EVENT_TIME_CHANGED,
|
||||
{ha.ATTR_NOW: test_time + timedelta(seconds=5)})
|
||||
assert sun.STATE_ABOVE_HORIZON == \
|
||||
hass.states.get(sun.ENTITY_ID).state
|
||||
|
||||
self.hass.block_till_done()
|
||||
|
||||
assert sun.STATE_ABOVE_HORIZON == \
|
||||
self.hass.states.get(sun.ENTITY_ID).state
|
||||
async def test_norway_in_june(hass):
|
||||
"""Test location in Norway where the sun doesn't set in summer."""
|
||||
hass.config.latitude = 69.6
|
||||
hass.config.longitude = 18.8
|
||||
|
||||
def test_norway_in_june(self):
|
||||
"""Test location in Norway where the sun doesn't set in summer."""
|
||||
self.hass.config.latitude = 69.6
|
||||
self.hass.config.longitude = 18.8
|
||||
june = datetime(2016, 6, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
june = datetime(2016, 6, 1, tzinfo=dt_util.UTC)
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=june):
|
||||
assert await async_setup_component(hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
|
||||
with patch('homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=june):
|
||||
assert setup_component(self.hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
state = hass.states.get(sun.ENTITY_ID)
|
||||
assert state is not None
|
||||
|
||||
state = self.hass.states.get(sun.ENTITY_ID)
|
||||
assert state is not None
|
||||
assert dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_RISING]) == \
|
||||
datetime(2016, 7, 25, 23, 23, 39, tzinfo=dt_util.UTC)
|
||||
assert dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_SETTING]) == \
|
||||
datetime(2016, 7, 26, 22, 19, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
assert dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_RISING]) == \
|
||||
datetime(2016, 7, 25, 23, 23, 39, tzinfo=dt_util.UTC)
|
||||
assert dt_util.parse_datetime(
|
||||
state.attributes[sun.STATE_ATTR_NEXT_SETTING]) == \
|
||||
datetime(2016, 7, 26, 22, 19, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
@mark.skip
|
||||
async def test_state_change_count(hass):
|
||||
"""Count the number of state change events in a location."""
|
||||
# Skipped because it's a bit slow. Has been validated with
|
||||
# multiple lattitudes and dates
|
||||
hass.config.latitude = 10
|
||||
hass.config.longitude = 0
|
||||
|
||||
now = datetime(2016, 6, 1, tzinfo=dt_util.UTC)
|
||||
|
||||
with patch(
|
||||
'homeassistant.helpers.condition.dt_util.utcnow',
|
||||
return_value=now):
|
||||
assert await async_setup_component(hass, sun.DOMAIN, {
|
||||
sun.DOMAIN: {sun.CONF_ELEVATION: 0}})
|
||||
|
||||
events = []
|
||||
@ha.callback
|
||||
def state_change_listener(event):
|
||||
if event.data.get('entity_id') == 'sun.sun':
|
||||
events.append(event)
|
||||
hass.bus.async_listen(EVENT_STATE_CHANGED, state_change_listener)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
for _ in range(24*60*60):
|
||||
now += timedelta(seconds=1)
|
||||
hass.bus.async_fire(
|
||||
ha.EVENT_TIME_CHANGED,
|
||||
{ha.ATTR_NOW: now})
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert len(events) < 721
|
||||
|
|
Loading…
Reference in New Issue