Times of The Day Binary Sensor ()

* First commit

* Times of the Day binary sensor added

* Python 3.5 fixes and logging removed

* Code refactored according to reviewer's suggestions

* Fixed config template with friendly name support

* Finall pep8 fixes

* Removed async_generate_entity_id and moved initial calculation to async_added_to_hass

* Change the configuration schema to follow the current requirements

* Update according latest suggestsion

* Fix typos and minor changes

* Fix lint issue
pull/21094/head
Klaudiusz Staniek 2019-02-15 14:13:44 +01:00 committed by Fabian Affolter
parent f3786e2f2b
commit 7b19428279
2 changed files with 1056 additions and 0 deletions
homeassistant/components/binary_sensor
tests/components/binary_sensor

View File

@ -0,0 +1,217 @@
"""Support for representing current time of the day as binary sensors."""
from datetime import datetime, timedelta
import logging
import pytz
import voluptuous as vol
from homeassistant.components.binary_sensor import (
PLATFORM_SCHEMA, BinarySensorDevice)
from homeassistant.const import (
CONF_AFTER, CONF_BEFORE, CONF_NAME, SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET)
from homeassistant.core import callback
import homeassistant.helpers.config_validation as cv
from homeassistant.helpers.event import async_track_point_in_utc_time
from homeassistant.helpers.sun import (
get_astral_event_date, get_astral_event_next)
from homeassistant.util import dt as dt_util
_LOGGER = logging.getLogger(__name__)
ATTR_AFTER = 'after'
ATTR_BEFORE = 'before'
ATTR_NEXT_UPDATE = 'next_update'
CONF_AFTER_OFFSET = 'after_offset'
CONF_BEFORE_OFFSET = 'before_offset'
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
vol.Required(CONF_AFTER):
vol.Any(cv.time, vol.All(vol.Lower, cv.sun_event)),
vol.Required(CONF_BEFORE):
vol.Any(cv.time, vol.All(vol.Lower, cv.sun_event)),
vol.Required(CONF_NAME): cv.string,
vol.Optional(CONF_AFTER_OFFSET, default=timedelta(0)): cv.time_period,
vol.Optional(CONF_BEFORE_OFFSET, default=timedelta(0)): cv.time_period,
})
async def async_setup_platform(
hass, config, async_add_entities, discovery_info=None):
"""Set up the ToD sensors."""
if hass.config.time_zone is None:
_LOGGER.error("Timezone is not set in Home Assistant configuration")
return
after = config[CONF_AFTER]
after_offset = config[CONF_AFTER_OFFSET]
before = config[CONF_BEFORE]
before_offset = config[CONF_BEFORE_OFFSET]
name = config[CONF_NAME]
sensor = TodSensor(name, after, after_offset, before, before_offset)
async_add_entities([sensor])
def is_sun_event(event):
"""Return true if event is sun event not time."""
return event in (SUN_EVENT_SUNRISE, SUN_EVENT_SUNSET)
class TodSensor(BinarySensorDevice):
"""Time of the Day Sensor."""
def __init__(self, name, after, after_offset, before, before_offset):
"""Init the ToD Sensor..."""
self._name = name
self._time_before = self._time_after = self._next_update = None
self._after_offset = after_offset
self._before_offset = before_offset
self._before = before
self._after = after
@property
def should_poll(self):
"""Sensor does not need to be polled."""
return False
@property
def name(self):
"""Return the name of the sensor."""
return self._name
@property
def after(self):
"""Return the timestamp for the begining of the period."""
return self._time_after
@property
def before(self):
"""Return the timestamp for the end of the period."""
return self._time_before
@property
def is_on(self):
"""Return True is sensor is on."""
if self.after < self.before:
return self.after <= self.current_datetime < self.before
return False
@property
def current_datetime(self):
"""Return local current datetime according to hass configuration."""
return dt_util.utcnow()
@property
def next_update(self):
"""Return the next update point in the UTC time."""
return self._next_update
@property
def device_state_attributes(self):
"""Return the state attributes of the sensor."""
return {
ATTR_AFTER: self.after.astimezone(
self.hass.config.time_zone).isoformat(),
ATTR_BEFORE: self.before.astimezone(
self.hass.config.time_zone).isoformat(),
ATTR_NEXT_UPDATE: self.next_update.astimezone(
self.hass.config.time_zone).isoformat(),
}
def _calculate_initial_boudary_time(self):
"""Calculate internal absolute time boudaries."""
nowutc = self.current_datetime
# 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.hass, self._after, nowutc) or \
get_astral_event_next(self.hass, 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 hass configured TZ not system wide
after_event_date = datetime.combine(
nowutc, self._after.replace(
tzinfo=self.hass.config.time_zone)).astimezone(tz=pytz.UTC)
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.hass, self._before, nowutc) or \
get_astral_event_next(self.hass, 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.hass, self._before, after_event_date)
else:
# Convert local time provided to UTC today, see above
before_event_date = datetime.combine(
nowutc, self._before.replace(
tzinfo=self.hass.config.time_zone)).astimezone(tz=pytz.UTC)
# 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 _turn_to_next_day(self):
"""Turn to to the next day."""
if is_sun_event(self._after):
self._time_after = get_astral_event_next(
self.hass, self._after,
self._time_after - self._after_offset)
self._time_after += self._after_offset
else:
# Offset is already there
self._time_after += timedelta(days=1)
if is_sun_event(self._before):
self._time_before = get_astral_event_next(
self.hass, self._before,
self._time_before - self._before_offset)
self._time_before += self._before_offset
else:
# Offset is already there
self._time_before += timedelta(days=1)
async def async_added_to_hass(self):
"""Call when entity about to be added to Home Assistant."""
await super().async_added_to_hass()
self._calculate_initial_boudary_time()
self._calculate_next_update()
self._point_in_time_listener(dt_util.now())
def _calculate_next_update(self):
"""Datetime when the next update to the state."""
now = self.current_datetime
if now < self.after:
self._next_update = self.after
return
if now < self.before:
self._next_update = self.before
return
self._turn_to_next_day()
self._next_update = self.after
@callback
def _point_in_time_listener(self, now):
"""Run when the state of the sensor should be updated."""
self._calculate_next_update()
self.async_schedule_update_ha_state()
async_track_point_in_utc_time(
self.hass, self._point_in_time_listener, self.next_update)

View File

@ -0,0 +1,839 @@
"""Test Times of the Day Binary Sensor."""
import unittest
from unittest.mock import patch
from datetime import timedelta, datetime
import pytz
from homeassistant import setup
import homeassistant.core as ha
from homeassistant.const import STATE_OFF, STATE_ON
import homeassistant.util.dt as dt_util
from homeassistant.setup import setup_component
from tests.common import (
get_test_home_assistant, assert_setup_component)
from homeassistant.helpers.sun import (
get_astral_event_date, get_astral_event_next)
class TestBinarySensorTod(unittest.TestCase):
"""Test for Binary sensor tod platform."""
hass = None
# pylint: disable=invalid-name
def setup_method(self, method):
"""Set up things to be run when tests are started."""
self.hass = get_test_home_assistant()
self.hass.config.latitute = 50.27583
self.hass.config.longitude = 18.98583
def teardown_method(self, method):
"""Stop everything that was started."""
self.hass.stop()
def test_setup(self):
"""Test the setup."""
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Early Morning',
'after': 'sunrise',
'after_offset': '-02:00',
'before': '7:00',
'before_offset': '1:00'
},
{
'platform': 'tod',
'name': 'Morning',
'after': 'sunrise',
'before': '12:00'
}
],
}
with assert_setup_component(2):
assert setup.setup_component(
self.hass, 'binary_sensor', config)
def test_setup_no_sensors(self):
"""Test setup with no sensors."""
with assert_setup_component(0):
assert setup.setup_component(self.hass, 'binary_sensor', {
'binary_sensor': {
'platform': 'tod'
}
})
def test_in_period_on_start(self):
"""Test simple setting."""
test_time = datetime(
2019, 1, 10, 18, 43, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Evening',
'after': '18:00',
'before': '22:00'
}
]
}
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.evening')
assert state.state == STATE_ON
def test_midnight_turnover_before_midnight_inside_period(self):
"""Test midnight turnover setting before midnight inside period ."""
test_time = datetime(
2019, 1, 10, 22, 30, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Night',
'after': '22:00',
'before': '5:00'
},
]
}
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_ON
def test_midnight_turnover_after_midnight_inside_period(self):
"""Test midnight turnover setting before midnight inside period ."""
test_time = datetime(
2019, 1, 10, 21, 00, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Night',
'after': '22:00',
'before': '5:00'
},
]
}
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time):
setup_component(self.hass, 'binary_sensor', config)
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_OFF
self.hass.block_till_done()
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time + timedelta(hours=1)):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: test_time + timedelta(hours=1)})
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_ON
def test_midnight_turnover_before_midnight_outside_period(self):
"""Test midnight turnover setting before midnight outside period."""
test_time = datetime(
2019, 1, 10, 20, 30, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Night',
'after': '22:00',
'before': '5:00'
}
]
}
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_OFF
def test_midnight_turnover_after_midnight_outside_period(self):
"""Test midnight turnover setting before midnight inside period ."""
test_time = datetime(
2019, 1, 10, 20, 0, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Night',
'after': '22:00',
'before': '5:00'
}
]
}
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=test_time):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_OFF
switchover_time = datetime(
2019, 1, 11, 4, 59, 0, tzinfo=self.hass.config.time_zone)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=switchover_time):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: switchover_time})
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_ON
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=switchover_time + timedelta(
minutes=1, seconds=1)):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: switchover_time + timedelta(
minutes=1, seconds=1)})
self.hass.block_till_done()
state = self.hass.states.get('binary_sensor.night')
assert state.state == STATE_OFF
def test_from_sunrise_to_sunset(self):
"""Test period from sunrise to sunset."""
test_time = datetime(
2019, 1, 12, tzinfo=self.hass.config.time_zone)
sunrise = dt_util.as_local(get_astral_event_date(
self.hass, 'sunrise', dt_util.as_utc(test_time)))
sunset = dt_util.as_local(get_astral_event_date(
self.hass, 'sunset', dt_util.as_utc(test_time)))
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Day',
'after': 'sunrise',
'before': 'sunset'
}
]
}
entity_id = 'binary_sensor.day'
testtime = sunrise + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
def test_from_sunset_to_sunrise(self):
"""Test period from sunset to sunrise."""
test_time = datetime(
2019, 1, 12, tzinfo=self.hass.config.time_zone)
sunset = dt_util.as_local(get_astral_event_date(
self.hass, 'sunset', test_time))
sunrise = dt_util.as_local(get_astral_event_next(
self.hass, 'sunrise', sunset))
# assert sunset == sunrise
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Night',
'after': 'sunset',
'before': 'sunrise'
}
]
}
entity_id = 'binary_sensor.night'
testtime = sunset + timedelta(minutes=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunset
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunset + timedelta(minutes=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise + timedelta(minutes=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
self.hass.block_till_done()
# assert state == "dupa"
assert state.state == STATE_OFF
testtime = sunrise + timedelta(minutes=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
def test_offset(self):
"""Test offset."""
after = datetime(
2019, 1, 10, 18, 0, 0,
tzinfo=self.hass.config.time_zone) + \
timedelta(hours=1, minutes=34)
before = datetime(
2019, 1, 10, 22, 0, 0,
tzinfo=self.hass.config.time_zone) + \
timedelta(hours=1, minutes=45)
entity_id = 'binary_sensor.evening'
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Evening',
'after': '18:00',
'after_offset': '1:34',
'before': '22:00',
'before_offset': '1:45'
}
]
}
testtime = after + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = after
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = before + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = before
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = before + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
def test_offset_overnight(self):
"""Test offset overnight."""
after = datetime(
2019, 1, 10, 18, 0, 0,
tzinfo=self.hass.config.time_zone) + \
timedelta(hours=1, minutes=34)
entity_id = 'binary_sensor.evening'
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Evening',
'after': '18:00',
'after_offset': '1:34',
'before': '22:00',
'before_offset': '3:00'
}
]
}
testtime = after + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = after
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
def test_norwegian_case_winter(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
test_time = datetime(2010, 1, 1, tzinfo=self.hass.config.time_zone)
sunrise = dt_util.as_local(get_astral_event_next(
self.hass, 'sunrise', dt_util.as_utc(test_time)))
sunset = dt_util.as_local(get_astral_event_next(
self.hass, 'sunset', dt_util.as_utc(test_time)))
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Day',
'after': 'sunrise',
'before': 'sunset'
}
]
}
entity_id = 'binary_sensor.day'
testtime = test_time
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
def test_norwegian_case_summer(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
test_time = datetime(2010, 6, 1, tzinfo=self.hass.config.time_zone)
sunrise = dt_util.as_local(get_astral_event_next(
self.hass, 'sunrise', dt_util.as_utc(test_time)))
sunset = dt_util.as_local(get_astral_event_next(
self.hass, 'sunset', dt_util.as_utc(test_time)))
print(sunrise)
print(sunset)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Day',
'after': 'sunrise',
'before': 'sunset'
}
]
}
entity_id = 'binary_sensor.day'
testtime = test_time
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
def test_sun_offset(self):
"""Test sun event with offset."""
test_time = datetime(
2019, 1, 12, tzinfo=self.hass.config.time_zone)
sunrise = dt_util.as_local(get_astral_event_date(
self.hass, 'sunrise', dt_util.as_utc(test_time)) +
timedelta(hours=-1, minutes=-30))
sunset = dt_util.as_local(get_astral_event_date(
self.hass, 'sunset', dt_util.as_utc(test_time)) +
timedelta(hours=1, minutes=30))
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Day',
'after': 'sunrise',
'after_offset': '-1:30',
'before': 'sunset',
'before_offset': '1:30'
}
]
}
entity_id = 'binary_sensor.day'
testtime = sunrise + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
testtime = sunrise + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=-1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
self.hass.block_till_done()
testtime = sunset
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
self.hass.block_till_done()
testtime = sunset + timedelta(seconds=1)
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_OFF
test_time = test_time + timedelta(days=1)
sunrise = dt_util.as_local(get_astral_event_date(
self.hass, 'sunrise', dt_util.as_utc(test_time)) +
timedelta(hours=-1, minutes=-30))
testtime = sunrise
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
self.hass.bus.fire(ha.EVENT_TIME_CHANGED, {
ha.ATTR_NOW: testtime})
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
assert state.state == STATE_ON
def test_dst(self):
"""Test sun event with offset."""
self.hass.config.time_zone = pytz.timezone('CET')
test_time = datetime(
2019, 3, 30, 3, 0, 0, tzinfo=self.hass.config.time_zone)
config = {
'binary_sensor': [
{
'platform': 'tod',
'name': 'Day',
'after': '2:30',
'before': '2:40'
}
]
}
# after 2019-03-30 03:00 CET the next update should ge scheduled
# at 3:30 not 2:30 local time
# Internally the
entity_id = 'binary_sensor.day'
testtime = test_time
with patch('homeassistant.components.binary_sensor.tod.dt_util.utcnow',
return_value=testtime):
setup_component(self.hass, 'binary_sensor', config)
self.hass.block_till_done()
state = self.hass.states.get(entity_id)
state.attributes['after'] == '2019-03-31T03:30:00+02:00'
state.attributes['before'] == '2019-03-31T03:40:00+02:00'
state.attributes['next_update'] == '2019-03-31T03:30:00+02:00'
assert state.state == STATE_OFF