-
-
Notifications
You must be signed in to change notification settings - Fork 31.8k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Add Holiday integration * Localize holiday names * Changes based on review feedback * Add tests * Add device info * Bump holidays to 0.36 * Default to Home Assistant country setting * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/holiday/config_flow.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * black * Move time * Stop creating duplicate holiday calendars * Set default language using python-holiday * Use common translation * Set _attr_name to None to fix friendly name * Fix location * Update homeassistant/components/holiday/__init__.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update tests/components/holiday/test_init.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * cleanup * Set up the integration and test the state * Test that configuring more than one instance is rejected * Set default_language to user's language, fallback to country's default language * Improve tests * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Cleanup * Add next year so we don't run out * Update tests/components/holiday/test_init.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Cleanup * Set default language in `__init__` * Add strict typing * Change default language: HA's language `en` is `en_US` in holidays, apart from Canada * CONF_PROVINCE can be None * Fix test * Fix default_language * Refactor tests * Province can be None * Add test for translated title * Address feedback * Address feedback * Change test to use service call * Address feedback * Apply suggestions from code review Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Changes based on review feedback * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Update homeassistant/components/holiday/calendar.py Co-authored-by: G Johansson <goran.johansson@shiftit.se> * Add a test if next event is missing * Rebase * Set device to service * Remove not needed translation key --------- Co-authored-by: G Johansson <goran.johansson@shiftit.se>
- Loading branch information
1 parent
67784de
commit 244edb4
Showing
18 changed files
with
716 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Validating CODEOWNERS rules …
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
"""The Holiday integration.""" | ||
from __future__ import annotations | ||
|
||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import Platform | ||
from homeassistant.core import HomeAssistant | ||
|
||
PLATFORMS: list[Platform] = [Platform.CALENDAR] | ||
|
||
|
||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Set up Holiday from a config entry.""" | ||
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS) | ||
|
||
return True | ||
|
||
|
||
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool: | ||
"""Unload a config entry.""" | ||
return await hass.config_entries.async_unload_platforms(entry, PLATFORMS) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
"""Holiday Calendar.""" | ||
from __future__ import annotations | ||
|
||
from datetime import datetime | ||
|
||
from holidays import HolidayBase, country_holidays | ||
|
||
from homeassistant.components.calendar import CalendarEntity, CalendarEvent | ||
from homeassistant.config_entries import ConfigEntry | ||
from homeassistant.const import CONF_COUNTRY | ||
from homeassistant.core import HomeAssistant | ||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo | ||
from homeassistant.helpers.entity_platform import AddEntitiesCallback | ||
from homeassistant.util import dt as dt_util | ||
|
||
from .const import CONF_PROVINCE, DOMAIN | ||
|
||
|
||
async def async_setup_entry( | ||
hass: HomeAssistant, | ||
config_entry: ConfigEntry, | ||
async_add_entities: AddEntitiesCallback, | ||
) -> None: | ||
"""Set up the Holiday Calendar config entry.""" | ||
country: str = config_entry.data[CONF_COUNTRY] | ||
province: str | None = config_entry.data.get(CONF_PROVINCE) | ||
language = hass.config.language | ||
|
||
obj_holidays = country_holidays( | ||
country, | ||
subdiv=province, | ||
years={dt_util.now().year, dt_util.now().year + 1}, | ||
language=language, | ||
) | ||
if language == "en": | ||
for lang in obj_holidays.supported_languages: | ||
if lang.startswith("en"): | ||
obj_holidays = country_holidays( | ||
country, | ||
subdiv=province, | ||
years={dt_util.now().year, dt_util.now().year + 1}, | ||
language=lang, | ||
) | ||
language = lang | ||
break | ||
|
||
async_add_entities( | ||
[ | ||
HolidayCalendarEntity( | ||
config_entry.title, | ||
country, | ||
province, | ||
language, | ||
obj_holidays, | ||
config_entry.entry_id, | ||
) | ||
], | ||
True, | ||
) | ||
|
||
|
||
class HolidayCalendarEntity(CalendarEntity): | ||
"""Representation of a Holiday Calendar element.""" | ||
|
||
_attr_has_entity_name = True | ||
_attr_name = None | ||
|
||
def __init__( | ||
self, | ||
name: str, | ||
country: str, | ||
province: str | None, | ||
language: str, | ||
obj_holidays: HolidayBase, | ||
unique_id: str, | ||
) -> None: | ||
"""Initialize HolidayCalendarEntity.""" | ||
self._country = country | ||
self._province = province | ||
self._location = name | ||
self._language = language | ||
self._attr_unique_id = unique_id | ||
self._attr_device_info = DeviceInfo( | ||
identifiers={(DOMAIN, unique_id)}, | ||
entry_type=DeviceEntryType.SERVICE, | ||
name=name, | ||
) | ||
self._obj_holidays = obj_holidays | ||
|
||
@property | ||
def event(self) -> CalendarEvent | None: | ||
"""Return the next upcoming event.""" | ||
next_holiday = None | ||
for holiday_date, holiday_name in sorted( | ||
self._obj_holidays.items(), key=lambda x: x[0] | ||
): | ||
if holiday_date >= dt_util.now().date(): | ||
next_holiday = (holiday_date, holiday_name) | ||
break | ||
|
||
if next_holiday is None: | ||
return None | ||
|
||
return CalendarEvent( | ||
summary=next_holiday[1], | ||
start=next_holiday[0], | ||
end=next_holiday[0], | ||
location=self._location, | ||
) | ||
|
||
async def async_get_events( | ||
self, hass: HomeAssistant, start_date: datetime, end_date: datetime | ||
) -> list[CalendarEvent]: | ||
"""Get all events in a specific time frame.""" | ||
obj_holidays = country_holidays( | ||
self._country, | ||
subdiv=self._province, | ||
years=list({start_date.year, end_date.year}), | ||
language=self._language, | ||
) | ||
|
||
event_list: list[CalendarEvent] = [] | ||
|
||
for holiday_date, holiday_name in obj_holidays.items(): | ||
if start_date.date() <= holiday_date <= end_date.date(): | ||
event = CalendarEvent( | ||
summary=holiday_name, | ||
start=holiday_date, | ||
end=holiday_date, | ||
location=self._location, | ||
) | ||
event_list.append(event) | ||
|
||
return event_list |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
"""Config flow for Holiday integration.""" | ||
from __future__ import annotations | ||
|
||
from typing import Any | ||
|
||
from babel import Locale | ||
from holidays import list_supported_countries | ||
import voluptuous as vol | ||
|
||
from homeassistant import config_entries | ||
from homeassistant.const import CONF_COUNTRY | ||
from homeassistant.data_entry_flow import FlowResult | ||
from homeassistant.helpers.selector import ( | ||
CountrySelector, | ||
CountrySelectorConfig, | ||
SelectSelector, | ||
SelectSelectorConfig, | ||
SelectSelectorMode, | ||
) | ||
|
||
from .const import CONF_PROVINCE, DOMAIN | ||
|
||
SUPPORTED_COUNTRIES = list_supported_countries(include_aliases=False) | ||
|
||
|
||
class HolidayConfigFlow(config_entries.ConfigFlow, domain=DOMAIN): | ||
"""Handle a config flow for Holiday.""" | ||
|
||
VERSION = 1 | ||
|
||
data: dict[str, Any] = {} | ||
|
||
async def async_step_user( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the initial step.""" | ||
if user_input is not None: | ||
self.data = user_input | ||
|
||
selected_country = self.data[CONF_COUNTRY] | ||
|
||
if SUPPORTED_COUNTRIES[selected_country]: | ||
return await self.async_step_province() | ||
|
||
self._async_abort_entries_match({CONF_COUNTRY: user_input[CONF_COUNTRY]}) | ||
|
||
locale = Locale(self.hass.config.language) | ||
title = locale.territories[selected_country] | ||
return self.async_create_entry(title=title, data=self.data) | ||
|
||
user_schema = vol.Schema( | ||
{ | ||
vol.Optional( | ||
CONF_COUNTRY, default=self.hass.config.country | ||
): CountrySelector( | ||
CountrySelectorConfig( | ||
countries=list(SUPPORTED_COUNTRIES), | ||
) | ||
), | ||
} | ||
) | ||
|
||
return self.async_show_form(step_id="user", data_schema=user_schema) | ||
|
||
async def async_step_province( | ||
self, user_input: dict[str, Any] | None = None | ||
) -> FlowResult: | ||
"""Handle the province step.""" | ||
if user_input is not None: | ||
combined_input: dict[str, Any] = {**self.data, **user_input} | ||
|
||
country = combined_input[CONF_COUNTRY] | ||
province = combined_input.get(CONF_PROVINCE) | ||
|
||
self._async_abort_entries_match( | ||
{ | ||
CONF_COUNTRY: country, | ||
CONF_PROVINCE: province, | ||
} | ||
) | ||
|
||
locale = Locale(self.hass.config.language) | ||
province_str = f", {province}" if province else "" | ||
name = f"{locale.territories[country]}{province_str}" | ||
|
||
return self.async_create_entry(title=name, data=combined_input) | ||
|
||
province_schema = vol.Schema( | ||
{ | ||
vol.Optional(CONF_PROVINCE): SelectSelector( | ||
SelectSelectorConfig( | ||
options=SUPPORTED_COUNTRIES[self.data[CONF_COUNTRY]], | ||
mode=SelectSelectorMode.DROPDOWN, | ||
) | ||
), | ||
} | ||
) | ||
|
||
return self.async_show_form(step_id="province", data_schema=province_schema) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
"""Constants for the Holiday integration.""" | ||
from typing import Final | ||
|
||
DOMAIN: Final = "holiday" | ||
|
||
CONF_PROVINCE: Final = "province" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"domain": "holiday", | ||
"name": "Holiday", | ||
"codeowners": ["@jrieger"], | ||
"config_flow": true, | ||
"documentation": "https://www.home-assistant.io/integrations/holiday", | ||
"iot_class": "local_polling", | ||
"requirements": ["holidays==0.37", "babel==2.13.1"] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
{ | ||
"config": { | ||
"abort": { | ||
"already_configured": "Already configured. Only a single configuration for country/province combination possible." | ||
}, | ||
"step": { | ||
"user": { | ||
"data": { | ||
"country": "Country" | ||
} | ||
}, | ||
"province": { | ||
"data": { | ||
"province": "Province" | ||
} | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
"""Tests for the Holiday integration.""" |
Oops, something went wrong.