diff --git a/CODEOWNERS b/CODEOWNERS index 52f137483031741d95d799961505bb9110c6bb69..1df0d2741cdfdee25be504514a9f812ea9d875ad 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -367,6 +367,7 @@ homeassistant/components/websocket_api/* @home-assistant/core homeassistant/components/wemo/* @sqldiablo homeassistant/components/withings/* @vangorra homeassistant/components/wled/* @frenck +homeassistant/components/workday/* @fabaff homeassistant/components/worldclock/* @fabaff homeassistant/components/wwlln/* @bachya homeassistant/components/xbox_live/* @MartinHjelmare diff --git a/homeassistant/components/workday/binary_sensor.py b/homeassistant/components/workday/binary_sensor.py index 866d87d62401f3a52eb3a8294f10b8e2aa293007..0aa1f5bfc428385c7a9fd440113bc00e001eccd8 100644 --- a/homeassistant/components/workday/binary_sensor.py +++ b/homeassistant/components/workday/binary_sensor.py @@ -1,6 +1,7 @@ """Sensor to indicate whether the current day is a workday.""" from datetime import datetime, timedelta import logging +from typing import Any import holidays import voluptuous as vol @@ -11,111 +12,6 @@ import homeassistant.helpers.config_validation as cv _LOGGER = logging.getLogger(__name__) -# List of all countries currently supported by holidays -# Source: https://github.com/dr-prodigy/python-holidays#available-countries -# There seems to be no way to get the list out at runtime -ALL_COUNTRIES = [ - "Argentina", - "AR", - "Aruba", - "AW", - "Australia", - "AU", - "Austria", - "AT", - "Brazil", - "BR", - "Belarus", - "BY", - "Belgium", - "BE", - "Bulgaria", - "BG", - "Canada", - "CA", - "Colombia", - "CO", - "Croatia", - "HR", - "Czech", - "CZ", - "Denmark", - "DK", - "England", - "Estonia", - "EE", - "EuropeanCentralBank", - "ECB", - "TAR", - "Finland", - "FI", - "France", - "FRA", - "Germany", - "DE", - "Hungary", - "HU", - "Honduras", - "HND", - "Iceland", - "IS", - "India", - "IND", - "Ireland", - "IE", - "Isle of Man", - "Italy", - "IT", - "Japan", - "JP", - "Kenya", - "KE", - "Lithuania", - "LT", - "Luxembourg", - "LU", - "Mexico", - "MX", - "Netherlands", - "NL", - "NewZealand", - "NZ", - "Northern Ireland", - "Norway", - "NO", - "Peru", - "PE", - "Poland", - "Polish", - "PL", - "Portugal", - "PT", - "PortugalExt", - "PTE", - "Russia", - "RU", - "Scotland", - "Slovenia", - "SI", - "Slovakia", - "SK", - "South Africa", - "ZA", - "Spain", - "ES", - "Sweden", - "SE", - "Switzerland", - "CH", - "Ukraine", - "UA", - "UnitedKingdom", - "UK", - "UnitedStates", - "US", - "Wales", -] - ALLOWED_DAYS = WEEKDAYS + ["holiday"] CONF_COUNTRY = "country" @@ -132,9 +28,28 @@ DEFAULT_EXCLUDES = ["sat", "sun", "holiday"] DEFAULT_NAME = "Workday Sensor" DEFAULT_OFFSET = 0 + +def valid_country(value: Any) -> str: + """Validate that the given country is supported.""" + value = cv.string(value) + all_supported_countries = holidays.list_supported_countries() + + try: + raw_value = value.encode("utf-8") + except UnicodeError: + raise vol.Invalid( + "The country name or the abbreviation must be a valid UTF-8 string." + ) + if not raw_value: + raise vol.Invalid("Country name or the abbreviation must not be empty.") + if value not in all_supported_countries: + raise vol.Invalid("Country is not supported.") + return value + + PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( { - vol.Required(CONF_COUNTRY): vol.In(ALL_COUNTRIES), + vol.Required(CONF_COUNTRY): valid_country, vol.Optional(CONF_EXCLUDES, default=DEFAULT_EXCLUDES): vol.All( cv.ensure_list, [vol.In(ALLOWED_DAYS)] ), @@ -151,13 +66,13 @@ PLATFORM_SCHEMA = PLATFORM_SCHEMA.extend( def setup_platform(hass, config, add_entities, discovery_info=None): """Set up the Workday sensor.""" - sensor_name = config.get(CONF_NAME) - country = config.get(CONF_COUNTRY) - province = config.get(CONF_PROVINCE) - workdays = config.get(CONF_WORKDAYS) - excludes = config.get(CONF_EXCLUDES) - days_offset = config.get(CONF_OFFSET) add_holidays = config.get(CONF_ADD_HOLIDAYS) + country = config[CONF_COUNTRY] + days_offset = config[CONF_OFFSET] + excludes = config[CONF_EXCLUDES] + province = config.get(CONF_PROVINCE) + sensor_name = config[CONF_NAME] + workdays = config[CONF_WORKDAYS] year = (get_date(datetime.today()) + timedelta(days=days_offset)).year obj_holidays = getattr(holidays, country)(years=year) @@ -259,7 +174,7 @@ class IsWorkdaySensor(BinarySensorDevice): # Default is no workday self._state = False - # Get iso day of the week (1 = Monday, 7 = Sunday) + # Get ISO day of the week (1 = Monday, 7 = Sunday) date = get_date(datetime.today()) + timedelta(days=self._days_offset) day = date.isoweekday() - 1 day_of_week = day_to_string(day) diff --git a/homeassistant/components/workday/manifest.json b/homeassistant/components/workday/manifest.json index 4b407e9523540c5b4e1b712e8325a1e3baf116e9..ac3bee7d07c928c3fc4802b7133de3e96b750f4d 100644 --- a/homeassistant/components/workday/manifest.json +++ b/homeassistant/components/workday/manifest.json @@ -3,8 +3,8 @@ "name": "Workday", "documentation": "https://www.home-assistant.io/integrations/workday", "requirements": [ - "holidays==0.9.11" + "holidays==0.9.12" ], "dependencies": [], - "codeowners": [] + "codeowners": ["@fabaff"] } \ No newline at end of file diff --git a/requirements_all.txt b/requirements_all.txt index 86fd67cc5bd51063ab8323df3ce372261259322f..4cd122e85835fce67f2670c3b50430e78a4419bd 100644 --- a/requirements_all.txt +++ b/requirements_all.txt @@ -663,7 +663,7 @@ hlk-sw16==0.0.7 hole==0.5.0 # homeassistant.components.workday -holidays==0.9.11 +holidays==0.9.12 # homeassistant.components.frontend home-assistant-frontend==20191204.1 diff --git a/requirements_test_all.txt b/requirements_test_all.txt index 6b9a7c9870277278097b825fdc5987f52925b568..77936dd47291fe1794a80a3acff29ad70b1c08d3 100644 --- a/requirements_test_all.txt +++ b/requirements_test_all.txt @@ -228,7 +228,7 @@ herepy==2.0.0 hole==0.5.0 # homeassistant.components.workday -holidays==0.9.11 +holidays==0.9.12 # homeassistant.components.frontend home-assistant-frontend==20191204.1 diff --git a/tests/components/workday/test_binary_sensor.py b/tests/components/workday/test_binary_sensor.py index 19da0cbfd877063f4d3f526ff369a086ece51bac..81ae18bfd3b28784caea4aedb0e4f8bf4ad979ce 100644 --- a/tests/components/workday/test_binary_sensor.py +++ b/tests/components/workday/test_binary_sensor.py @@ -2,7 +2,10 @@ from datetime import date from unittest.mock import patch -from homeassistant.components.workday.binary_sensor import day_to_string +import pytest +import voluptuous as vol + +import homeassistant.components.workday.binary_sensor as binary_sensor from homeassistant.setup import setup_component from tests.common import assert_setup_component, get_test_home_assistant @@ -68,6 +71,20 @@ class TestWorkdaySetup: """Stop everything that was started.""" self.hass.stop() + def test_valid_country(self): + """Test topic name/filter validation.""" + # Invalid UTF-8, must not contain U+D800 to U+DFFF + with pytest.raises(vol.Invalid): + binary_sensor.valid_country("\ud800") + with pytest.raises(vol.Invalid): + binary_sensor.valid_country("\udfff") + # Country MUST NOT be empty + with pytest.raises(vol.Invalid): + binary_sensor.valid_country("") + # Country must be supported by holidays + with pytest.raises(vol.Invalid): + binary_sensor.valid_country("HomeAssistantLand") + def test_setup_component_province(self): """Set up workday component.""" with assert_setup_component(1, "binary_sensor"): @@ -214,7 +231,7 @@ class TestWorkdaySetup: 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 + assert binary_sensor.day_to_string(0) == "mon" + assert binary_sensor.day_to_string(1) == "tue" + assert binary_sensor.day_to_string(7) == "holiday" + assert binary_sensor.day_to_string(8) is None