Add config flow to Workday (#72558)
* Initial commit Workday Config Flow * Add tests * Remove day_to_string * new entity name, new depr. version, clean * Use repairs for depr. warning * Fix issue_registry moved * tweaks * hassfest * Fix CI * FlowResultType * breaking version * remove translation * Fixes * naming * duplicates * abort entries match * add_suggested_values_to_schema * various * validate country * abort_entries_match in option flow * Remove country test * remove country not exist string * docstring exceptions * easier * break version * unneeded check * slim tests * Fix import test * Fix province in abort_match * review comments * Fix import province * Add review fixes * fix reviews * Review fixes
This commit is contained in:
parent
a511e7d6bc
commit
f74103c57e
13 changed files with 1093 additions and 20 deletions
|
@ -1 +1,28 @@
|
|||
"""Sensor to indicate whether the current day is a workday."""
|
||||
from __future__ import annotations
|
||||
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.core import HomeAssistant
|
||||
|
||||
from .const import PLATFORMS
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up Workday from a config entry."""
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(async_update_listener))
|
||||
|
||||
hass.config_entries.async_setup_platforms(entry, PLATFORMS)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
async def async_update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener for options."""
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
|
||||
|
||||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Unload Workday config entry."""
|
||||
|
||||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
|
||||
|
|
|
@ -12,10 +12,14 @@ from homeassistant.components.binary_sensor import (
|
|||
PLATFORM_SCHEMA as PARENT_PLATFORM_SCHEMA,
|
||||
BinarySensorEntity,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_IMPORT, ConfigEntry
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
from homeassistant.helpers.device_registry import DeviceEntryType
|
||||
from homeassistant.helpers.entity import DeviceInfo
|
||||
from homeassistant.helpers.entity_platform import AddEntitiesCallback
|
||||
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
|
||||
from homeassistant.helpers.typing import ConfigType, DiscoveryInfoType
|
||||
from homeassistant.util import dt
|
||||
|
||||
|
@ -32,6 +36,7 @@ from .const import (
|
|||
DEFAULT_NAME,
|
||||
DEFAULT_OFFSET,
|
||||
DEFAULT_WORKDAYS,
|
||||
DOMAIN,
|
||||
LOGGER,
|
||||
)
|
||||
|
||||
|
@ -76,21 +81,44 @@ PLATFORM_SCHEMA = PARENT_PLATFORM_SCHEMA.extend(
|
|||
)
|
||||
|
||||
|
||||
def setup_platform(
|
||||
async def async_setup_platform(
|
||||
hass: HomeAssistant,
|
||||
config: ConfigType,
|
||||
add_entities: AddEntitiesCallback,
|
||||
async_add_entities: AddEntitiesCallback,
|
||||
discovery_info: DiscoveryInfoType | None = None,
|
||||
) -> None:
|
||||
"""Set up the Workday sensor."""
|
||||
add_holidays: list[DateLike] = config[CONF_ADD_HOLIDAYS]
|
||||
remove_holidays: list[str] = config[CONF_REMOVE_HOLIDAYS]
|
||||
country: str = config[CONF_COUNTRY]
|
||||
days_offset: int = config[CONF_OFFSET]
|
||||
excludes: list[str] = config[CONF_EXCLUDES]
|
||||
province: str | None = config.get(CONF_PROVINCE)
|
||||
sensor_name: str = config[CONF_NAME]
|
||||
workdays: list[str] = config[CONF_WORKDAYS]
|
||||
async_create_issue(
|
||||
hass,
|
||||
DOMAIN,
|
||||
"deprecated_yaml",
|
||||
breaks_in_ha_version="2023.7.0",
|
||||
is_fixable=False,
|
||||
severity=IssueSeverity.WARNING,
|
||||
translation_key="deprecated_yaml",
|
||||
)
|
||||
|
||||
hass.async_create_task(
|
||||
hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": SOURCE_IMPORT},
|
||||
data=config,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
async def async_setup_entry(
|
||||
hass: HomeAssistant, entry: ConfigEntry, async_add_entities: AddEntitiesCallback
|
||||
) -> None:
|
||||
"""Set up the Workday sensor."""
|
||||
add_holidays: list[DateLike] = entry.options[CONF_ADD_HOLIDAYS]
|
||||
remove_holidays: list[str] = entry.options[CONF_REMOVE_HOLIDAYS]
|
||||
country: str = entry.options[CONF_COUNTRY]
|
||||
days_offset: int = int(entry.options[CONF_OFFSET])
|
||||
excludes: list[str] = entry.options[CONF_EXCLUDES]
|
||||
province: str | None = entry.options.get(CONF_PROVINCE)
|
||||
sensor_name: str = entry.options[CONF_NAME]
|
||||
workdays: list[str] = entry.options[CONF_WORKDAYS]
|
||||
|
||||
year: int = (dt.now() + timedelta(days=days_offset)).year
|
||||
obj_holidays: HolidayBase = getattr(holidays, country)(years=year)
|
||||
|
@ -131,8 +159,17 @@ def setup_platform(
|
|||
_holiday_string = holiday_date.strftime("%Y-%m-%d")
|
||||
LOGGER.debug("%s %s", _holiday_string, name)
|
||||
|
||||
add_entities(
|
||||
[IsWorkdaySensor(obj_holidays, workdays, excludes, days_offset, sensor_name)],
|
||||
async_add_entities(
|
||||
[
|
||||
IsWorkdaySensor(
|
||||
obj_holidays,
|
||||
workdays,
|
||||
excludes,
|
||||
days_offset,
|
||||
sensor_name,
|
||||
entry.entry_id,
|
||||
)
|
||||
],
|
||||
True,
|
||||
)
|
||||
|
||||
|
@ -140,6 +177,8 @@ def setup_platform(
|
|||
class IsWorkdaySensor(BinarySensorEntity):
|
||||
"""Implementation of a Workday sensor."""
|
||||
|
||||
_attr_has_entity_name = True
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
obj_holidays: HolidayBase,
|
||||
|
@ -147,9 +186,9 @@ class IsWorkdaySensor(BinarySensorEntity):
|
|||
excludes: list[str],
|
||||
days_offset: int,
|
||||
name: str,
|
||||
entry_id: str,
|
||||
) -> None:
|
||||
"""Initialize the Workday sensor."""
|
||||
self._attr_name = name
|
||||
self._obj_holidays = obj_holidays
|
||||
self._workdays = workdays
|
||||
self._excludes = excludes
|
||||
|
@ -159,6 +198,14 @@ class IsWorkdaySensor(BinarySensorEntity):
|
|||
CONF_EXCLUDES: excludes,
|
||||
CONF_OFFSET: days_offset,
|
||||
}
|
||||
self._attr_unique_id = entry_id
|
||||
self._attr_device_info = DeviceInfo(
|
||||
entry_type=DeviceEntryType.SERVICE,
|
||||
identifiers={(DOMAIN, entry_id)},
|
||||
manufacturer="python-holidays",
|
||||
model=holidays.__version__,
|
||||
name=name,
|
||||
)
|
||||
|
||||
def is_include(self, day: str, now: date) -> bool:
|
||||
"""Check if given day is in the includes list."""
|
||||
|
|
308
homeassistant/components/workday/config_flow.py
Normal file
308
homeassistant/components/workday/config_flow.py
Normal file
|
@ -0,0 +1,308 @@
|
|||
"""Adds config flow for Workday integration."""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any
|
||||
|
||||
import holidays
|
||||
from holidays import HolidayBase
|
||||
import voluptuous as vol
|
||||
|
||||
from homeassistant.config_entries import (
|
||||
ConfigEntry,
|
||||
ConfigFlow,
|
||||
OptionsFlowWithConfigEntry,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import callback
|
||||
from homeassistant.data_entry_flow import AbortFlow, FlowResult
|
||||
from homeassistant.exceptions import HomeAssistantError
|
||||
from homeassistant.helpers.selector import (
|
||||
NumberSelector,
|
||||
NumberSelectorConfig,
|
||||
NumberSelectorMode,
|
||||
SelectSelector,
|
||||
SelectSelectorConfig,
|
||||
SelectSelectorMode,
|
||||
TextSelector,
|
||||
)
|
||||
from homeassistant.util import dt
|
||||
|
||||
from .const import (
|
||||
ALLOWED_DAYS,
|
||||
CONF_ADD_HOLIDAYS,
|
||||
CONF_COUNTRY,
|
||||
CONF_EXCLUDES,
|
||||
CONF_OFFSET,
|
||||
CONF_PROVINCE,
|
||||
CONF_REMOVE_HOLIDAYS,
|
||||
CONF_WORKDAYS,
|
||||
DEFAULT_EXCLUDES,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_OFFSET,
|
||||
DEFAULT_WORKDAYS,
|
||||
DOMAIN,
|
||||
)
|
||||
|
||||
NONE_SENTINEL = "none"
|
||||
|
||||
|
||||
def add_province_to_schema(
|
||||
schema: vol.Schema,
|
||||
options: dict[str, Any],
|
||||
) -> vol.Schema:
|
||||
"""Update schema with province from country."""
|
||||
year: int = dt.now().year
|
||||
obj_holidays: HolidayBase = getattr(holidays, options[CONF_COUNTRY])(years=year)
|
||||
if not obj_holidays.subdivisions:
|
||||
return schema
|
||||
|
||||
province_list = [NONE_SENTINEL, *obj_holidays.subdivisions]
|
||||
add_schema = {
|
||||
vol.Optional(CONF_PROVINCE, default=NONE_SENTINEL): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=province_list,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
translation_key=CONF_PROVINCE,
|
||||
)
|
||||
),
|
||||
}
|
||||
|
||||
return vol.Schema({**DATA_SCHEMA_OPT.schema, **add_schema})
|
||||
|
||||
|
||||
def validate_custom_dates(user_input: dict[str, Any]) -> None:
|
||||
"""Validate custom dates for add/remove holidays."""
|
||||
|
||||
for add_date in user_input[CONF_ADD_HOLIDAYS]:
|
||||
if dt.parse_date(add_date) is None:
|
||||
raise AddDatesError("Incorrect date")
|
||||
|
||||
year: int = dt.now().year
|
||||
obj_holidays: HolidayBase = getattr(holidays, user_input[CONF_COUNTRY])(years=year)
|
||||
if user_input.get(CONF_PROVINCE):
|
||||
obj_holidays = getattr(holidays, user_input[CONF_COUNTRY])(
|
||||
subdiv=user_input[CONF_PROVINCE], years=year
|
||||
)
|
||||
|
||||
for remove_date in user_input[CONF_REMOVE_HOLIDAYS]:
|
||||
if dt.parse_date(remove_date) is None:
|
||||
if obj_holidays.get_named(remove_date) == []:
|
||||
raise RemoveDatesError("Incorrect date or name")
|
||||
|
||||
|
||||
DATA_SCHEMA_SETUP = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_NAME, default=DEFAULT_NAME): TextSelector(),
|
||||
vol.Required(CONF_COUNTRY): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=list(holidays.list_supported_countries()),
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
DATA_SCHEMA_OPT = vol.Schema(
|
||||
{
|
||||
vol.Optional(CONF_EXCLUDES, default=DEFAULT_EXCLUDES): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=ALLOWED_DAYS,
|
||||
multiple=True,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
translation_key="days",
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_OFFSET, default=DEFAULT_OFFSET): NumberSelector(
|
||||
NumberSelectorConfig(min=-10, max=10, step=1, mode=NumberSelectorMode.BOX)
|
||||
),
|
||||
vol.Optional(CONF_WORKDAYS, default=DEFAULT_WORKDAYS): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=ALLOWED_DAYS,
|
||||
multiple=True,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
translation_key="days",
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_ADD_HOLIDAYS, default=[]): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=[],
|
||||
multiple=True,
|
||||
custom_value=True,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
)
|
||||
),
|
||||
vol.Optional(CONF_REMOVE_HOLIDAYS, default=[]): SelectSelector(
|
||||
SelectSelectorConfig(
|
||||
options=[],
|
||||
multiple=True,
|
||||
custom_value=True,
|
||||
mode=SelectSelectorMode.DROPDOWN,
|
||||
)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
class WorkdayConfigFlow(ConfigFlow, domain=DOMAIN):
|
||||
"""Handle a config flow for Workday integration."""
|
||||
|
||||
VERSION = 1
|
||||
|
||||
data: dict[str, Any] = {}
|
||||
|
||||
@staticmethod
|
||||
@callback
|
||||
def async_get_options_flow(
|
||||
config_entry: ConfigEntry,
|
||||
) -> WorkdayOptionsFlowHandler:
|
||||
"""Get the options flow for this handler."""
|
||||
return WorkdayOptionsFlowHandler(config_entry)
|
||||
|
||||
async def async_step_import(self, config: dict[str, Any]) -> FlowResult:
|
||||
"""Import a configuration from config.yaml."""
|
||||
|
||||
abort_match = {
|
||||
CONF_COUNTRY: config[CONF_COUNTRY],
|
||||
CONF_EXCLUDES: config[CONF_EXCLUDES],
|
||||
CONF_OFFSET: config[CONF_OFFSET],
|
||||
CONF_WORKDAYS: config[CONF_WORKDAYS],
|
||||
CONF_ADD_HOLIDAYS: config[CONF_ADD_HOLIDAYS],
|
||||
CONF_REMOVE_HOLIDAYS: config[CONF_REMOVE_HOLIDAYS],
|
||||
CONF_PROVINCE: config.get(CONF_PROVINCE),
|
||||
}
|
||||
new_config = config.copy()
|
||||
new_config[CONF_PROVINCE] = config.get(CONF_PROVINCE)
|
||||
|
||||
self._async_abort_entries_match(abort_match)
|
||||
return await self.async_step_options(user_input=new_config)
|
||||
|
||||
async def async_step_user(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle the user initial step."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is not None:
|
||||
self.data = user_input
|
||||
return await self.async_step_options()
|
||||
return self.async_show_form(
|
||||
step_id="user",
|
||||
data_schema=DATA_SCHEMA_SETUP,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
async def async_step_options(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Handle remaining flow."""
|
||||
errors: dict[str, str] = {}
|
||||
if user_input is not None:
|
||||
combined_input: dict[str, Any] = {**self.data, **user_input}
|
||||
if combined_input.get(CONF_PROVINCE, NONE_SENTINEL) == NONE_SENTINEL:
|
||||
combined_input[CONF_PROVINCE] = None
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
validate_custom_dates, combined_input
|
||||
)
|
||||
except AddDatesError:
|
||||
errors["add_holidays"] = "add_holiday_error"
|
||||
except RemoveDatesError:
|
||||
errors["remove_holidays"] = "remove_holiday_error"
|
||||
except NotImplementedError:
|
||||
self.async_abort(reason="incorrect_province")
|
||||
|
||||
abort_match = {
|
||||
CONF_COUNTRY: combined_input[CONF_COUNTRY],
|
||||
CONF_EXCLUDES: combined_input[CONF_EXCLUDES],
|
||||
CONF_OFFSET: combined_input[CONF_OFFSET],
|
||||
CONF_WORKDAYS: combined_input[CONF_WORKDAYS],
|
||||
CONF_ADD_HOLIDAYS: combined_input[CONF_ADD_HOLIDAYS],
|
||||
CONF_REMOVE_HOLIDAYS: combined_input[CONF_REMOVE_HOLIDAYS],
|
||||
CONF_PROVINCE: combined_input[CONF_PROVINCE],
|
||||
}
|
||||
|
||||
self._async_abort_entries_match(abort_match)
|
||||
if not errors:
|
||||
return self.async_create_entry(
|
||||
title=combined_input[CONF_NAME],
|
||||
data={},
|
||||
options=combined_input,
|
||||
)
|
||||
|
||||
schema = await self.hass.async_add_executor_job(
|
||||
add_province_to_schema, DATA_SCHEMA_OPT, self.data
|
||||
)
|
||||
new_schema = self.add_suggested_values_to_schema(schema, user_input)
|
||||
return self.async_show_form(
|
||||
step_id="options",
|
||||
data_schema=new_schema,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
class WorkdayOptionsFlowHandler(OptionsFlowWithConfigEntry):
|
||||
"""Handle Workday options."""
|
||||
|
||||
async def async_step_init(
|
||||
self, user_input: dict[str, Any] | None = None
|
||||
) -> FlowResult:
|
||||
"""Manage Workday options."""
|
||||
errors: dict[str, str] = {}
|
||||
|
||||
if user_input is not None:
|
||||
combined_input: dict[str, Any] = {**self.options, **user_input}
|
||||
if combined_input.get(CONF_PROVINCE, NONE_SENTINEL) == NONE_SENTINEL:
|
||||
combined_input[CONF_PROVINCE] = None
|
||||
|
||||
try:
|
||||
await self.hass.async_add_executor_job(
|
||||
validate_custom_dates, combined_input
|
||||
)
|
||||
except AddDatesError:
|
||||
errors["add_holidays"] = "add_holiday_error"
|
||||
except RemoveDatesError:
|
||||
errors["remove_holidays"] = "remove_holiday_error"
|
||||
else:
|
||||
try:
|
||||
self._async_abort_entries_match(
|
||||
{
|
||||
CONF_COUNTRY: self._config_entry.options[CONF_COUNTRY],
|
||||
CONF_EXCLUDES: combined_input[CONF_EXCLUDES],
|
||||
CONF_OFFSET: combined_input[CONF_OFFSET],
|
||||
CONF_WORKDAYS: combined_input[CONF_WORKDAYS],
|
||||
CONF_ADD_HOLIDAYS: combined_input[CONF_ADD_HOLIDAYS],
|
||||
CONF_REMOVE_HOLIDAYS: combined_input[CONF_REMOVE_HOLIDAYS],
|
||||
CONF_PROVINCE: combined_input[CONF_PROVINCE],
|
||||
}
|
||||
)
|
||||
except AbortFlow as err:
|
||||
errors = {"base": err.reason}
|
||||
else:
|
||||
return self.async_create_entry(data=combined_input)
|
||||
|
||||
saved_options = self.options.copy()
|
||||
if saved_options[CONF_PROVINCE] is None:
|
||||
saved_options[CONF_PROVINCE] = NONE_SENTINEL
|
||||
schema: vol.Schema = await self.hass.async_add_executor_job(
|
||||
add_province_to_schema, DATA_SCHEMA_OPT, self.options
|
||||
)
|
||||
new_schema = self.add_suggested_values_to_schema(schema, user_input)
|
||||
|
||||
return self.async_show_form(
|
||||
step_id="init",
|
||||
data_schema=new_schema,
|
||||
errors=errors,
|
||||
)
|
||||
|
||||
|
||||
class AddDatesError(HomeAssistantError):
|
||||
"""Exception for error adding dates."""
|
||||
|
||||
|
||||
class RemoveDatesError(HomeAssistantError):
|
||||
"""Exception for error removing dates."""
|
||||
|
||||
|
||||
class CountryNotExist(HomeAssistantError):
|
||||
"""Exception country does not exist error."""
|
|
@ -3,12 +3,15 @@ from __future__ import annotations
|
|||
|
||||
import logging
|
||||
|
||||
from homeassistant.const import WEEKDAYS
|
||||
from homeassistant.const import WEEKDAYS, Platform
|
||||
|
||||
LOGGER = logging.getLogger(__package__)
|
||||
|
||||
ALLOWED_DAYS = WEEKDAYS + ["holiday"]
|
||||
|
||||
DOMAIN = "workday"
|
||||
PLATFORMS = [Platform.BINARY_SENSOR]
|
||||
|
||||
CONF_COUNTRY = "country"
|
||||
CONF_PROVINCE = "province"
|
||||
CONF_WORKDAYS = "workdays"
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"domain": "workday",
|
||||
"name": "Workday",
|
||||
"codeowners": ["@fabaff", "@gjohansson-ST"],
|
||||
"config_flow": true,
|
||||
"documentation": "https://www.home-assistant.io/integrations/workday",
|
||||
"iot_class": "local_polling",
|
||||
"loggers": [
|
||||
|
|
90
homeassistant/components/workday/strings.json
Normal file
90
homeassistant/components/workday/strings.json
Normal file
|
@ -0,0 +1,90 @@
|
|||
{
|
||||
"config": {
|
||||
"abort": {
|
||||
"incorrect_province": "Incorrect subdivision from yaml import"
|
||||
},
|
||||
"step": {
|
||||
"user": {
|
||||
"data": {
|
||||
"name": "[%key:common::config_flow::data::name%]",
|
||||
"country": "Country"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"data": {
|
||||
"excludes": "Excludes",
|
||||
"days_offset": "Offset",
|
||||
"workdays": "Workdays",
|
||||
"add_holidays": "Add holidays",
|
||||
"remove_holidays": "Remove Holidays",
|
||||
"province": "Subdivision of country"
|
||||
},
|
||||
"data_description": {
|
||||
"excludes": "List of workdays to exclude",
|
||||
"days_offset": "Days offset",
|
||||
"workdays": "List of workdays",
|
||||
"add_holidays": "Add custom holidays as YYYY-MM-DD",
|
||||
"remove_holidays": "Remove holidays as YYYY-MM-DD or by using partial of name",
|
||||
"province": "State, Terroritory, Province, Region of Country"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"add_holiday_error": "Incorrect format on date (YYYY-MM-DD)",
|
||||
"remove_holiday_error": "Incorrect format on date (YYYY-MM-DD) or holiday name not found",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
}
|
||||
},
|
||||
"options": {
|
||||
"step": {
|
||||
"init": {
|
||||
"data": {
|
||||
"excludes": "[%key:component::workday::config::step::options::data::excludes%]",
|
||||
"days_offset": "[%key:component::workday::config::step::options::data::days_offset%]",
|
||||
"workdays": "[%key:component::workday::config::step::options::data::workdays%]",
|
||||
"add_holidays": "[%key:component::workday::config::step::options::data::add_holidays%]",
|
||||
"remove_holidays": "[%key:component::workday::config::step::options::data::remove_holidays%]",
|
||||
"province": "[%key:component::workday::config::step::options::data::province%]"
|
||||
},
|
||||
"data_description": {
|
||||
"excludes": "[%key:component::workday::config::step::options::data_description::excludes%]",
|
||||
"days_offset": "[%key:component::workday::config::step::options::data_description::days_offset%]",
|
||||
"workdays": "[%key:component::workday::config::step::options::data_description::workdays%]",
|
||||
"add_holidays": "[%key:component::workday::config::step::options::data_description::add_holidays%]",
|
||||
"remove_holidays": "[%key:component::workday::config::step::options::data_description::remove_holidays%]",
|
||||
"province": "[%key:component::workday::config::step::options::data_description::province%]"
|
||||
}
|
||||
}
|
||||
},
|
||||
"error": {
|
||||
"add_holiday_error": "Incorrect format on date (YYYY-MM-DD)",
|
||||
"remove_holiday_error": "Incorrect format on date (YYYY-MM-DD) or holiday name not found",
|
||||
"already_configured": "[%key:common::config_flow::abort::already_configured_service%]"
|
||||
}
|
||||
},
|
||||
"issues": {
|
||||
"deprecated_yaml": {
|
||||
"title": "The Workday YAML configuration is being removed",
|
||||
"description": "Configuring Workday using YAML is being removed.\n\nYour existing YAML configuration has been imported into the UI automatically.\n\nRemove the Workday YAML configuration from your configuration.yaml file and restart Home Assistant to fix this issue."
|
||||
}
|
||||
},
|
||||
"selector": {
|
||||
"province": {
|
||||
"options": {
|
||||
"none": "No subdivision"
|
||||
}
|
||||
},
|
||||
"days": {
|
||||
"options": {
|
||||
"mon": "Monday",
|
||||
"tue": "Tuesday",
|
||||
"wed": "Wednesday",
|
||||
"thu": "Thursday",
|
||||
"fri": "Friday",
|
||||
"sat": "Saturday",
|
||||
"sun": "Sunday",
|
||||
"holiday": "Holidays"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -500,6 +500,7 @@ FLOWS = {
|
|||
"wiz",
|
||||
"wled",
|
||||
"wolflink",
|
||||
"workday",
|
||||
"ws66i",
|
||||
"xbox",
|
||||
"xiaomi_aqara",
|
||||
|
|
|
@ -6224,7 +6224,7 @@
|
|||
"workday": {
|
||||
"name": "Workday",
|
||||
"integration_type": "hub",
|
||||
"config_flow": false,
|
||||
"config_flow": true,
|
||||
"iot_class": "local_polling"
|
||||
},
|
||||
"worldclock": {
|
||||
|
|
|
@ -8,22 +8,37 @@ from homeassistant.components.workday.const import (
|
|||
DEFAULT_NAME,
|
||||
DEFAULT_OFFSET,
|
||||
DEFAULT_WORKDAYS,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.config_entries import SOURCE_USER
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.setup import async_setup_component
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
|
||||
async def init_integration(
|
||||
hass: HomeAssistant,
|
||||
config: dict[str, Any],
|
||||
) -> None:
|
||||
"""Set up the Workday integration in Home Assistant."""
|
||||
entry_id: str = "1",
|
||||
source: str = SOURCE_USER,
|
||||
) -> MockConfigEntry:
|
||||
"""Set up the Scrape integration in Home Assistant."""
|
||||
|
||||
await async_setup_component(
|
||||
hass, "binary_sensor", {"binary_sensor": {"platform": "workday", **config}}
|
||||
config_entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
source=source,
|
||||
data={},
|
||||
options=config,
|
||||
entry_id=entry_id,
|
||||
)
|
||||
|
||||
config_entry.add_to_hass(hass)
|
||||
|
||||
await hass.config_entries.async_setup(config_entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
return config_entry
|
||||
|
||||
|
||||
TEST_CONFIG_WITH_PROVINCE = {
|
||||
"name": DEFAULT_NAME,
|
||||
|
|
14
tests/components/workday/conftest.py
Normal file
14
tests/components/workday/conftest.py
Normal file
|
@ -0,0 +1,14 @@
|
|||
"""Fixtures for Workday integration tests."""
|
||||
from collections.abc import Generator
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def mock_setup_entry() -> Generator[AsyncMock, None, None]:
|
||||
"""Mock setting up a config entry."""
|
||||
with patch(
|
||||
"homeassistant.components.workday.async_setup_entry", return_value=True
|
||||
) as mock_setup:
|
||||
yield mock_setup
|
|
@ -79,6 +79,34 @@ async def test_setup(
|
|||
}
|
||||
|
||||
|
||||
async def test_setup_from_import(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test setup from various configs."""
|
||||
freezer.move_to(datetime(2022, 4, 15, 12, tzinfo=UTC)) # Monday
|
||||
await async_setup_component(
|
||||
hass,
|
||||
"binary_sensor",
|
||||
{
|
||||
"binary_sensor": {
|
||||
"platform": "workday",
|
||||
"country": "DE",
|
||||
}
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("binary_sensor.workday_sensor")
|
||||
assert state.state == "off"
|
||||
assert state.attributes == {
|
||||
"friendly_name": "Workday Sensor",
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
}
|
||||
|
||||
|
||||
async def test_setup_with_invalid_province_from_yaml(hass: HomeAssistant) -> None:
|
||||
"""Test setup invalid province with import."""
|
||||
|
||||
|
|
488
tests/components/workday/test_config_flow.py
Normal file
488
tests/components/workday/test_config_flow.py
Normal file
|
@ -0,0 +1,488 @@
|
|||
"""Test the Workday config flow."""
|
||||
from __future__ import annotations
|
||||
|
||||
import pytest
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.components.workday.const import (
|
||||
CONF_ADD_HOLIDAYS,
|
||||
CONF_COUNTRY,
|
||||
CONF_EXCLUDES,
|
||||
CONF_OFFSET,
|
||||
CONF_PROVINCE,
|
||||
CONF_REMOVE_HOLIDAYS,
|
||||
CONF_WORKDAYS,
|
||||
DEFAULT_EXCLUDES,
|
||||
DEFAULT_NAME,
|
||||
DEFAULT_OFFSET,
|
||||
DEFAULT_WORKDAYS,
|
||||
DOMAIN,
|
||||
)
|
||||
from homeassistant.const import CONF_NAME
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.data_entry_flow import FlowResultType
|
||||
|
||||
from . import init_integration
|
||||
|
||||
from tests.common import MockConfigEntry
|
||||
|
||||
pytestmark = pytest.mark.usefixtures("mock_setup_entry")
|
||||
|
||||
|
||||
async def test_form(hass: HomeAssistant) -> None:
|
||||
"""Test we get the forms."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_NAME: "Workday Sensor",
|
||||
CONF_COUNTRY: "DE",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
CONF_PROVINCE: "none",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result3["title"] == "Workday Sensor"
|
||||
assert result3["options"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
}
|
||||
|
||||
|
||||
async def test_form_no_subdivision(hass: HomeAssistant) -> None:
|
||||
"""Test we get the forms correctly without subdivision."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_NAME: "Workday Sensor",
|
||||
CONF_COUNTRY: "SE",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result3["title"] == "Workday Sensor"
|
||||
assert result3["options"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "SE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
}
|
||||
|
||||
|
||||
async def test_import_flow_success(hass: HomeAssistant) -> None:
|
||||
"""Test a successful import of yaml."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_NAME: DEFAULT_NAME,
|
||||
CONF_COUNTRY: "DE",
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result["title"] == "Workday Sensor"
|
||||
assert result["options"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
}
|
||||
|
||||
result2 = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_NAME: "Workday Sensor 2",
|
||||
CONF_COUNTRY: "DE",
|
||||
CONF_PROVINCE: "BW",
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result2["title"] == "Workday Sensor 2"
|
||||
assert result2["options"] == {
|
||||
"name": "Workday Sensor 2",
|
||||
"country": "DE",
|
||||
"province": "BW",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
}
|
||||
|
||||
|
||||
async def test_import_flow_already_exist(hass: HomeAssistant) -> None:
|
||||
"""Test import of yaml already exist."""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_NAME: "Workday sensor 2",
|
||||
CONF_COUNTRY: "DE",
|
||||
CONF_EXCLUDES: ["sat", "sun", "holiday"],
|
||||
CONF_OFFSET: 0,
|
||||
CONF_WORKDAYS: ["mon", "tue", "wed", "thu", "fri"],
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.ABORT
|
||||
assert result["reason"] == "already_configured"
|
||||
|
||||
|
||||
async def test_import_flow_province_no_conflict(hass: HomeAssistant) -> None:
|
||||
"""Test import of yaml with province."""
|
||||
|
||||
entry = MockConfigEntry(
|
||||
domain=DOMAIN,
|
||||
data={},
|
||||
options={
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
},
|
||||
)
|
||||
entry.add_to_hass(hass)
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN,
|
||||
context={"source": config_entries.SOURCE_IMPORT},
|
||||
data={
|
||||
CONF_NAME: "Workday sensor 2",
|
||||
CONF_COUNTRY: "DE",
|
||||
CONF_PROVINCE: "BW",
|
||||
CONF_EXCLUDES: ["sat", "sun", "holiday"],
|
||||
CONF_OFFSET: 0,
|
||||
CONF_WORKDAYS: ["mon", "tue", "wed", "thu", "fri"],
|
||||
CONF_ADD_HOLIDAYS: [],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result["type"] == FlowResultType.CREATE_ENTRY
|
||||
|
||||
|
||||
async def test_options_form(hass: HomeAssistant) -> None:
|
||||
"""Test we get the form in options."""
|
||||
|
||||
entry = await init_integration(
|
||||
hass,
|
||||
{
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": "BW",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result2["data"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": "BW",
|
||||
}
|
||||
|
||||
|
||||
async def test_form_incorrect_dates(hass: HomeAssistant) -> None:
|
||||
"""Test errors in setup entry."""
|
||||
|
||||
result = await hass.config_entries.flow.async_init(
|
||||
DOMAIN, context={"source": config_entries.SOURCE_USER}
|
||||
)
|
||||
assert result["type"] == FlowResultType.FORM
|
||||
|
||||
result2 = await hass.config_entries.flow.async_configure(
|
||||
result["flow_id"],
|
||||
{
|
||||
CONF_NAME: "Workday Sensor",
|
||||
CONF_COUNTRY: "DE",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: ["2022-xx-12"],
|
||||
CONF_REMOVE_HOLIDAYS: [],
|
||||
CONF_PROVINCE: "none",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
assert result3["errors"] == {"add_holidays": "add_holiday_error"}
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: ["2022-12-12"],
|
||||
CONF_REMOVE_HOLIDAYS: ["Does not exist"],
|
||||
CONF_PROVINCE: "none",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["errors"] == {"remove_holidays": "remove_holiday_error"}
|
||||
|
||||
result3 = await hass.config_entries.flow.async_configure(
|
||||
result2["flow_id"],
|
||||
{
|
||||
CONF_EXCLUDES: DEFAULT_EXCLUDES,
|
||||
CONF_OFFSET: DEFAULT_OFFSET,
|
||||
CONF_WORKDAYS: DEFAULT_WORKDAYS,
|
||||
CONF_ADD_HOLIDAYS: ["2022-12-12"],
|
||||
CONF_REMOVE_HOLIDAYS: ["Weihnachtstag"],
|
||||
CONF_PROVINCE: "none",
|
||||
},
|
||||
)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
assert result3["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result3["title"] == "Workday Sensor"
|
||||
assert result3["options"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2022-12-12"],
|
||||
"remove_holidays": ["Weihnachtstag"],
|
||||
"province": None,
|
||||
}
|
||||
|
||||
|
||||
async def test_options_form_incorrect_dates(hass: HomeAssistant) -> None:
|
||||
"""Test errors in options."""
|
||||
|
||||
entry = await init_integration(
|
||||
hass,
|
||||
{
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
},
|
||||
)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry.entry_id)
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2022-xx-12"],
|
||||
"remove_holidays": [],
|
||||
"province": "BW",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["errors"] == {"add_holidays": "add_holiday_error"}
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2022-12-12"],
|
||||
"remove_holidays": ["Does not exist"],
|
||||
"province": "BW",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["errors"] == {"remove_holidays": "remove_holiday_error"}
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2022-12-12"],
|
||||
"remove_holidays": ["Weihnachtstag"],
|
||||
"province": "BW",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == FlowResultType.CREATE_ENTRY
|
||||
assert result2["data"] == {
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2022-12-12"],
|
||||
"remove_holidays": ["Weihnachtstag"],
|
||||
"province": "BW",
|
||||
}
|
||||
|
||||
|
||||
async def test_options_form_abort_duplicate(hass: HomeAssistant) -> None:
|
||||
"""Test errors in options for duplicates."""
|
||||
|
||||
await init_integration(
|
||||
hass,
|
||||
{
|
||||
"name": "Workday Sensor",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
},
|
||||
entry_id="1",
|
||||
)
|
||||
entry2 = await init_integration(
|
||||
hass,
|
||||
{
|
||||
"name": "Workday Sensor2",
|
||||
"country": "DE",
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": ["2023-03-28"],
|
||||
"remove_holidays": [],
|
||||
"province": None,
|
||||
},
|
||||
entry_id="2",
|
||||
)
|
||||
|
||||
result = await hass.config_entries.options.async_init(entry2.entry_id)
|
||||
|
||||
result2 = await hass.config_entries.options.async_configure(
|
||||
result["flow_id"],
|
||||
user_input={
|
||||
"excludes": ["sat", "sun", "holiday"],
|
||||
"days_offset": 0.0,
|
||||
"workdays": ["mon", "tue", "wed", "thu", "fri"],
|
||||
"add_holidays": [],
|
||||
"remove_holidays": [],
|
||||
"province": "none",
|
||||
},
|
||||
)
|
||||
|
||||
assert result2["type"] == FlowResultType.FORM
|
||||
assert result2["errors"] == {"base": "already_configured"}
|
51
tests/components/workday/test_init.py
Normal file
51
tests/components/workday/test_init.py
Normal file
|
@ -0,0 +1,51 @@
|
|||
"""Test Workday component setup process."""
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from freezegun.api import FrozenDateTimeFactory
|
||||
|
||||
from homeassistant import config_entries
|
||||
from homeassistant.core import HomeAssistant
|
||||
from homeassistant.util.dt import UTC
|
||||
|
||||
from . import TEST_CONFIG_EXAMPLE_1, TEST_CONFIG_WITH_PROVINCE, init_integration
|
||||
|
||||
|
||||
async def test_load_unload_entry(hass: HomeAssistant) -> None:
|
||||
"""Test load and unload entry."""
|
||||
entry = await init_integration(hass, TEST_CONFIG_EXAMPLE_1)
|
||||
|
||||
state = hass.states.get("binary_sensor.workday_sensor")
|
||||
assert state
|
||||
|
||||
await hass.config_entries.async_remove(entry.entry_id)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
state = hass.states.get("binary_sensor.workday_sensor")
|
||||
assert not state
|
||||
|
||||
|
||||
async def test_update_options(
|
||||
hass: HomeAssistant,
|
||||
freezer: FrozenDateTimeFactory,
|
||||
) -> None:
|
||||
"""Test options update and config entry is reloaded."""
|
||||
freezer.move_to(datetime(2023, 4, 12, 12, tzinfo=UTC)) # Monday
|
||||
|
||||
entry = await init_integration(hass, TEST_CONFIG_WITH_PROVINCE)
|
||||
assert entry.state == config_entries.ConfigEntryState.LOADED
|
||||
assert entry.update_listeners is not None
|
||||
state = hass.states.get("binary_sensor.workday_sensor")
|
||||
assert state.state == "on"
|
||||
|
||||
new_options = TEST_CONFIG_WITH_PROVINCE.copy()
|
||||
new_options["add_holidays"] = ["2023-04-12"]
|
||||
|
||||
hass.config_entries.async_update_entry(entry, options=new_options)
|
||||
await hass.async_block_till_done()
|
||||
|
||||
entry_check = hass.config_entries.async_get_entry("1")
|
||||
assert entry_check.state == config_entries.ConfigEntryState.LOADED
|
||||
state = hass.states.get("binary_sensor.workday_sensor")
|
||||
assert state.state == "off"
|
Loading…
Add table
Reference in a new issue