parent
774fd19638
commit
509cfb6433
4 changed files with 314 additions and 0 deletions
157
homeassistant/components/binary_sensor/workday.py
Normal file
157
homeassistant/components/binary_sensor/workday.py
Normal file
|
@ -0,0 +1,157 @@
|
|||
"""Sensor to indicate whether the current day is a workday."""
|
||||
import asyncio
|
||||
import logging
|
||||
import datetime
|
||||
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.components.sensor import PLATFORM_SCHEMA
|
||||
from homeassistant.const import (STATE_ON, STATE_OFF, STATE_UNKNOWN,
|
||||
CONF_NAME, WEEKDAYS)
|
||||
import homeassistant.util.dt as dt_util
|
||||
from homeassistant.helpers.entity import Entity
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
REQUIREMENTS = ['holidays==0.8.1']
|
||||
|
||||
# List of all countries currently supported by holidays
|
||||
# There seems to be no way to get the list out at runtime
|
||||
ALL_COUNTRIES = ['Australia', 'AU', 'Austria', 'AT', 'Canada', 'CA',
|
||||
'Colombia', 'CO', 'Czech', 'CZ', 'Denmark', 'DK', 'England',
|
||||
'EuropeanCentralBank', 'ECB', 'TAR', 'Germany', 'DE',
|
||||
'Ireland', 'Isle of Man', 'Mexico', 'MX', 'Netherlands', 'NL',
|
||||
'NewZealand', 'NZ', 'Northern Ireland', 'Norway', 'NO',
|
||||
'Portugal', 'PT', 'PortugalExt', 'PTE', 'Scotland', 'Spain',
|
||||
'ES', 'UnitedKingdom', 'UK', 'UnitedStates', 'US', 'Wales']
|
||||
CONF_COUNTRY = 'country'
|
||||
CONF_PROVINCE = 'province'
|
||||
CONF_WORKDAYS = 'workdays'
|
||||
# By default, Monday - Friday are workdays
|
||||
DEFAULT_WORKDAYS = ['mon', 'tue', 'wed', 'thu', 'fri']
|
||||
CONF_EXCLUDES = 'excludes'
|
||||
# By default, public holidays, Saturdays and Sundays are excluded from workdays
|
||||
DEFAULT_EXCLUDES = ['sat', 'sun', 'holiday']
|
||||
DEFAULT_NAME = 'Workday Sensor'
|
||||
ALLOWED_DAYS = WEEKDAYS + ['holiday']
|
||||
|
||||
PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend({
|
||||
vol.Required(CONF_COUNTRY): vol.In(ALL_COUNTRIES),
|
||||
vol.Optional(CONF_PROVINCE, default=None): cv.string,
|
||||
vol.Optional(CONF_NAME, default=DEFAULT_NAME): cv.string,
|
||||
vol.Optional(CONF_WORKDAYS, default=DEFAULT_WORKDAYS):
|
||||
vol.All(cv.ensure_list, [vol.In(ALLOWED_DAYS)]),
|
||||
vol.Optional(CONF_EXCLUDES, default=DEFAULT_EXCLUDES):
|
||||
vol.All(cv.ensure_list, [vol.In(ALLOWED_DAYS)]),
|
||||
})
|
||||
|
||||
|
||||
def setup_platform(hass, config, add_devices, discovery_info=None):
|
||||
"""Setup the Workday sensor."""
|
||||
import holidays
|
||||
|
||||
# Get the Sensor name from the config
|
||||
sensor_name = config.get(CONF_NAME)
|
||||
|
||||
# Get the country code from the config
|
||||
country = config.get(CONF_COUNTRY)
|
||||
|
||||
# Get the province from the config
|
||||
province = config.get(CONF_PROVINCE)
|
||||
|
||||
# Get the list of workdays from the config
|
||||
workdays = config.get(CONF_WORKDAYS)
|
||||
|
||||
# Get the list of excludes from the config
|
||||
excludes = config.get(CONF_EXCLUDES)
|
||||
|
||||
# Instantiate the holidays module for the current year
|
||||
year = datetime.datetime.now().year
|
||||
obj_holidays = getattr(holidays, country)(years=year)
|
||||
|
||||
# Also apply the provience, if available for the configured country
|
||||
if province:
|
||||
if province not in obj_holidays.PROVINCES:
|
||||
_LOGGER.error('There is no province/state %s in country %s',
|
||||
province, country)
|
||||
return False
|
||||
else:
|
||||
year = datetime.datetime.now().year
|
||||
obj_holidays = getattr(holidays, country)(prov=province,
|
||||
years=year)
|
||||
|
||||
# Output found public holidays via the debug channel
|
||||
_LOGGER.debug("Found the following holidays for your configuration:")
|
||||
for date, name in sorted(obj_holidays.items()):
|
||||
_LOGGER.debug("%s %s", date, name)
|
||||
|
||||
# Add ourselves as device
|
||||
add_devices([IsWorkdaySensor(obj_holidays, workdays,
|
||||
excludes, sensor_name)], True)
|
||||
|
||||
|
||||
def day_to_string(day):
|
||||
"""Convert day index 0 - 7 to string."""
|
||||
try:
|
||||
return ALLOWED_DAYS[day]
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
|
||||
class IsWorkdaySensor(Entity):
|
||||
"""Implementation of a Workday sensor."""
|
||||
|
||||
def __init__(self, obj_holidays, workdays, excludes, name):
|
||||
"""Initialize the Workday sensor."""
|
||||
self._name = name
|
||||
self._obj_holidays = obj_holidays
|
||||
self._workdays = workdays
|
||||
self._excludes = excludes
|
||||
self._state = STATE_UNKNOWN
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
"""Return the name of the sensor."""
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def state(self):
|
||||
"""Return the state of the device."""
|
||||
return self._state
|
||||
|
||||
def is_include(self, day, now):
|
||||
"""Check if given day is in the includes list."""
|
||||
# Check includes
|
||||
if day in self._workdays:
|
||||
return True
|
||||
elif 'holiday' in self._workdays and now in self._obj_holidays:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def is_exclude(self, day, now):
|
||||
"""Check if given day is in the excludes list."""
|
||||
# Check excludes
|
||||
if day in self._excludes:
|
||||
return True
|
||||
elif 'holiday' in self._excludes and now in self._obj_holidays:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@asyncio.coroutine
|
||||
def async_update(self):
|
||||
"""Get date and look whether it is a holiday."""
|
||||
# Default is no workday
|
||||
self._state = STATE_OFF
|
||||
|
||||
# Get iso day of the week (1 = Monday, 7 = Sunday)
|
||||
day = datetime.datetime.today().isoweekday() - 1
|
||||
day_of_week = day_to_string(day)
|
||||
|
||||
if self.is_include(day_of_week, dt_util.now()):
|
||||
self._state = STATE_ON
|
||||
|
||||
if self.is_exclude(day_of_week, dt_util.now()):
|
||||
self._state = STATE_OFF
|
|
@ -215,6 +215,9 @@ heatmiserV3==0.9.1
|
|||
# homeassistant.components.switch.hikvisioncam
|
||||
hikvision==0.4
|
||||
|
||||
# homeassistant.components.binary_sensor.workday
|
||||
holidays==0.8.1
|
||||
|
||||
# homeassistant.components.sensor.dht
|
||||
# http://github.com/adafruit/Adafruit_Python_DHT/archive/da8cddf7fb629c1ef4f046ca44f42523c9cf2d11.zip#Adafruit_DHT==1.3.0
|
||||
|
||||
|
|
|
@ -17,3 +17,4 @@ requests_mock>=1.0
|
|||
mock-open>=1.3.1
|
||||
flake8-docstrings==1.0.2
|
||||
asynctest>=0.8.0
|
||||
freezegun>=0.3.8
|
||||
|
|
153
tests/components/binary_sensor/test_workday.py
Normal file
153
tests/components/binary_sensor/test_workday.py
Normal file
|
@ -0,0 +1,153 @@
|
|||
"""Tests the HASS workday binary sensor."""
|
||||
from freezegun import freeze_time
|
||||
from homeassistant.components.binary_sensor.workday import day_to_string
|
||||
from homeassistant.setup import setup_component
|
||||
|
||||
from tests.common import (
|
||||
get_test_home_assistant, assert_setup_component)
|
||||
|
||||
|
||||
class TestWorkdaySetup(object):
|
||||
"""Test class for workday sensor."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Setup things to be run when tests are started."""
|
||||
self.hass = get_test_home_assistant()
|
||||
|
||||
# Set valid default config for test
|
||||
self.config_province = {
|
||||
'binary_sensor': {
|
||||
'platform': 'workday',
|
||||
'country': 'DE',
|
||||
'province': 'BW'
|
||||
},
|
||||
}
|
||||
|
||||
self.config_noprovince = {
|
||||
'binary_sensor': {
|
||||
'platform': 'workday',
|
||||
'country': 'DE',
|
||||
},
|
||||
}
|
||||
|
||||
self.config_invalidprovince = {
|
||||
'binary_sensor': {
|
||||
'platform': 'workday',
|
||||
'country': 'DE',
|
||||
'province': 'invalid'
|
||||
},
|
||||
}
|
||||
|
||||
self.config_includeholiday = {
|
||||
'binary_sensor': {
|
||||
'platform': 'workday',
|
||||
'country': 'DE',
|
||||
'province': 'BW',
|
||||
'workdays': ['holiday', 'mon', 'tue', 'wed', 'thu', 'fri'],
|
||||
'excludes': ['sat', 'sun']
|
||||
},
|
||||
}
|
||||
|
||||
def teardown_method(self):
|
||||
"""Stop everything that was started."""
|
||||
self.hass.stop()
|
||||
|
||||
def test_setup_component_province(self):
|
||||
"""Setup workday component."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_province)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
# Freeze time to a workday
|
||||
@freeze_time("Mar 15th, 2017")
|
||||
def test_workday_province(self):
|
||||
"""Test if workdays are reported correctly."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_province)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
self.hass.start()
|
||||
|
||||
entity = self.hass.states.get('binary_sensor.workday_sensor')
|
||||
assert entity.state == 'on'
|
||||
|
||||
# Freeze time to a weekend
|
||||
@freeze_time("Mar 12th, 2017")
|
||||
def test_weekend_province(self):
|
||||
"""Test if weekends are reported correctly."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_province)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
self.hass.start()
|
||||
|
||||
entity = self.hass.states.get('binary_sensor.workday_sensor')
|
||||
assert entity.state == 'off'
|
||||
|
||||
# Freeze time to a public holiday in province BW
|
||||
@freeze_time("Jan 6th, 2017")
|
||||
def test_public_holiday_province(self):
|
||||
"""Test if public holidays are reported correctly."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_province)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
self.hass.start()
|
||||
|
||||
entity = self.hass.states.get('binary_sensor.workday_sensor')
|
||||
assert entity.state == 'off'
|
||||
|
||||
def test_setup_component_noprovince(self):
|
||||
"""Setup workday component."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_noprovince)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
# Freeze time to a public holiday in province BW
|
||||
@freeze_time("Jan 6th, 2017")
|
||||
def test_public_holiday_noprovince(self):
|
||||
"""Test if public holidays are reported correctly."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor', self.config_noprovince)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
self.hass.start()
|
||||
|
||||
entity = self.hass.states.get('binary_sensor.workday_sensor')
|
||||
assert entity.state == 'on'
|
||||
|
||||
def test_setup_component_invalidprovince(self):
|
||||
"""Setup workday component."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor',
|
||||
self.config_invalidprovince)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is None
|
||||
|
||||
# Freeze time to a public holiday in province BW
|
||||
@freeze_time("Jan 6th, 2017")
|
||||
def test_public_holiday_includeholiday(self):
|
||||
"""Test if public holidays are reported correctly."""
|
||||
with assert_setup_component(1, 'binary_sensor'):
|
||||
setup_component(self.hass, 'binary_sensor',
|
||||
self.config_includeholiday)
|
||||
|
||||
assert self.hass.states.get('binary_sensor.workday_sensor') is not None
|
||||
|
||||
self.hass.start()
|
||||
|
||||
entity = self.hass.states.get('binary_sensor.workday_sensor')
|
||||
assert entity.state == 'on'
|
||||
|
||||
def test_day_to_string(self):
|
||||
"""Test if day_to_string is behaving correctly."""
|
||||
assert day_to_string(0) == 'mon'
|
||||
assert day_to_string(1) == 'tue'
|
||||
assert day_to_string(7) == 'holiday'
|
||||
assert day_to_string(8) is None
|
Loading…
Add table
Reference in a new issue