Move holiday object to runtime data in workday

This commit is contained in:
G Johansson 2025-07-20 10:20:23 +00:00
parent 507f29a209
commit 0d3a9592b4
2 changed files with 107 additions and 116 deletions

View File

@ -2,9 +2,10 @@
from __future__ import annotations
from datetime import timedelta
from functools import partial
from holidays import HolidayBase, country_holidays
from holidays import PUBLIC, HolidayBase, country_holidays
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE
@ -12,12 +13,18 @@ from homeassistant.core import HomeAssistant
from homeassistant.exceptions import ConfigEntryError
from homeassistant.helpers.issue_registry import IssueSeverity, async_create_issue
from homeassistant.setup import SetupPhases, async_pause_setup
from homeassistant.util import dt as dt_util
from .const import CONF_PROVINCE, DOMAIN, PLATFORMS
from .const import CONF_CATEGORY, CONF_OFFSET, CONF_PROVINCE, DOMAIN, LOGGER, PLATFORMS
type WorkdayConfigEntry = ConfigEntry[HolidayBase]
async def _async_validate_country_and_province(
hass: HomeAssistant, entry: ConfigEntry, country: str | None, province: str | None
hass: HomeAssistant,
entry: WorkdayConfigEntry,
country: str | None,
province: str | None,
) -> None:
"""Validate country and province."""
@ -73,32 +80,107 @@ async def _async_validate_country_and_province(
) from ex
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
def _get_obj_holidays(
country: str | None,
province: str | None,
year: int,
language: str | None,
categories: list[str] | None,
) -> HolidayBase:
"""Get the object for the requested country and year."""
if not country:
return HolidayBase()
set_categories = None
if categories:
category_list = [PUBLIC]
category_list.extend(categories)
set_categories = tuple(category_list)
obj_holidays: HolidayBase = country_holidays(
country,
subdiv=province,
years=[year, year + 1],
language=language,
categories=set_categories,
)
supported_languages = obj_holidays.supported_languages
default_language = obj_holidays.default_language
if default_language and not language:
# If no language is set, use the default language
LOGGER.debug("Changing language from None to %s", default_language)
return country_holidays( # Return default if no language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
if (
default_language
and language
and language not in supported_languages
and language.startswith("en")
):
# If language does not match supported languages, use the first English variant
if default_language.startswith("en"):
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
for lang in supported_languages:
if lang.startswith("en"):
LOGGER.debug("Changing language from %s to %s", language, lang)
return country_holidays(
country,
subdiv=province,
years=year,
language=lang,
categories=set_categories,
)
if default_language and language and language not in supported_languages:
# If language does not match supported languages, use the default language
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
return obj_holidays
async def async_setup_entry(hass: HomeAssistant, entry: WorkdayConfigEntry) -> bool:
"""Set up Workday from a config entry."""
country: str | None = entry.options.get(CONF_COUNTRY)
province: str | None = entry.options.get(CONF_PROVINCE)
days_offset: int = int(entry.options[CONF_OFFSET])
year: int = (dt_util.now() + timedelta(days=days_offset)).year
language: str | None = entry.options.get(CONF_LANGUAGE)
categories: list[str] | None = entry.options.get(CONF_CATEGORY)
await _async_validate_country_and_province(hass, entry, country, province)
if country and CONF_LANGUAGE not in entry.options:
with async_pause_setup(hass, SetupPhases.WAIT_IMPORT_PACKAGES):
# import executor job is used here because multiple integrations use
# the holidays library and it is not thread safe to import it in parallel
# https://github.com/python/cpython/issues/83065
cls: HolidayBase = await hass.async_add_import_executor_job(
partial(country_holidays, country, subdiv=province)
)
default_language = cls.default_language
new_options = entry.options.copy()
new_options[CONF_LANGUAGE] = default_language
hass.config_entries.async_update_entry(entry, options=new_options)
entry.runtime_data = _get_obj_holidays(
country, province, year, language, categories
)
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
async def async_unload_entry(hass: HomeAssistant, entry: WorkdayConfigEntry) -> bool:
"""Unload Workday config entry."""
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS)

View File

@ -3,19 +3,13 @@
from __future__ import annotations
from datetime import date, datetime, timedelta
from typing import Final
from typing import TYPE_CHECKING, Final
from holidays import (
PUBLIC,
HolidayBase,
__version__ as python_holidays_version,
country_holidays,
)
from holidays import HolidayBase, __version__ as python_holidays_version
import voluptuous as vol
from homeassistant.components.binary_sensor import BinarySensorEntity
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import CONF_COUNTRY, CONF_LANGUAGE, CONF_NAME
from homeassistant.const import CONF_COUNTRY, CONF_NAME
from homeassistant.core import (
CALLBACK_TYPE,
HomeAssistant,
@ -36,16 +30,17 @@ from homeassistant.util import dt as dt_util, slugify
from .const import (
ALLOWED_DAYS,
CONF_ADD_HOLIDAYS,
CONF_CATEGORY,
CONF_EXCLUDES,
CONF_OFFSET,
CONF_PROVINCE,
CONF_REMOVE_HOLIDAYS,
CONF_WORKDAYS,
DOMAIN,
LOGGER,
)
if TYPE_CHECKING:
from . import WorkdayConfigEntry
SERVICE_CHECK_DATE: Final = "check_date"
CHECK_DATE: Final = "check_date"
@ -70,89 +65,9 @@ def validate_dates(holiday_list: list[str]) -> list[str]:
return calc_holidays
def _get_obj_holidays(
country: str | None,
province: str | None,
year: int,
language: str | None,
categories: list[str] | None,
) -> HolidayBase:
"""Get the object for the requested country and year."""
if not country:
return HolidayBase()
set_categories = None
if categories:
category_list = [PUBLIC]
category_list.extend(categories)
set_categories = tuple(category_list)
obj_holidays: HolidayBase = country_holidays(
country,
subdiv=province,
years=[year, year + 1],
language=language,
categories=set_categories,
)
supported_languages = obj_holidays.supported_languages
default_language = obj_holidays.default_language
if default_language and not language:
# If no language is set, use the default language
LOGGER.debug("Changing language from None to %s", default_language)
return country_holidays( # Return default if no language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
if (
default_language
and language
and language not in supported_languages
and language.startswith("en")
):
# If language does not match supported languages, use the first English variant
if default_language.startswith("en"):
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
for lang in supported_languages:
if lang.startswith("en"):
LOGGER.debug("Changing language from %s to %s", language, lang)
return country_holidays(
country,
subdiv=province,
years=year,
language=lang,
categories=set_categories,
)
if default_language and language and language not in supported_languages:
# If language does not match supported languages, use the default language
LOGGER.debug("Changing language from %s to %s", language, default_language)
return country_holidays( # Return default English if default language
country,
subdiv=province,
years=year,
language=default_language,
categories=set_categories,
)
return obj_holidays
async def async_setup_entry(
hass: HomeAssistant,
entry: ConfigEntry,
entry: WorkdayConfigEntry,
async_add_entities: AddConfigEntryEntitiesCallback,
) -> None:
"""Set up the Workday sensor."""
@ -161,16 +76,10 @@ async def async_setup_entry(
country: str | None = entry.options.get(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]
language: str | None = entry.options.get(CONF_LANGUAGE)
categories: list[str] | None = entry.options.get(CONF_CATEGORY)
year: int = (dt_util.now() + timedelta(days=days_offset)).year
obj_holidays: HolidayBase = await hass.async_add_executor_job(
_get_obj_holidays, country, province, year, language, categories
)
obj_holidays = entry.runtime_data
calc_add_holidays: list[str] = validate_dates(add_holidays)
calc_remove_holidays: list[str] = validate_dates(remove_holidays)
next_year = dt_util.now().year + 1