mirror of
https://github.com/home-assistant/core.git
synced 2025-09-07 22:01:34 +02:00
Add base entity to workday (#150329)
This commit is contained in:
@@ -2,32 +2,24 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from datetime import date, datetime, timedelta
|
from datetime import datetime
|
||||||
from typing import Final
|
from typing import Final
|
||||||
|
|
||||||
from holidays import HolidayBase, __version__ as python_holidays_version
|
from holidays import HolidayBase
|
||||||
import voluptuous as vol
|
import voluptuous as vol
|
||||||
|
|
||||||
from homeassistant.components.binary_sensor import BinarySensorEntity
|
from homeassistant.components.binary_sensor import BinarySensorEntity
|
||||||
from homeassistant.const import CONF_NAME
|
from homeassistant.const import CONF_NAME
|
||||||
from homeassistant.core import (
|
from homeassistant.core import HomeAssistant, SupportsResponse
|
||||||
CALLBACK_TYPE,
|
|
||||||
HomeAssistant,
|
|
||||||
ServiceResponse,
|
|
||||||
SupportsResponse,
|
|
||||||
callback,
|
|
||||||
)
|
|
||||||
from homeassistant.helpers import config_validation as cv
|
from homeassistant.helpers import config_validation as cv
|
||||||
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
|
||||||
from homeassistant.helpers.entity_platform import (
|
from homeassistant.helpers.entity_platform import (
|
||||||
AddConfigEntryEntitiesCallback,
|
AddConfigEntryEntitiesCallback,
|
||||||
async_get_current_platform,
|
async_get_current_platform,
|
||||||
)
|
)
|
||||||
from homeassistant.helpers.event import async_track_point_in_utc_time
|
|
||||||
from homeassistant.util import dt as dt_util
|
|
||||||
|
|
||||||
from . import WorkdayConfigEntry
|
from . import WorkdayConfigEntry
|
||||||
from .const import ALLOWED_DAYS, CONF_EXCLUDES, CONF_OFFSET, CONF_WORKDAYS, DOMAIN
|
from .const import CONF_EXCLUDES, CONF_OFFSET, CONF_WORKDAYS
|
||||||
|
from .entity import BaseWorkdayEntity
|
||||||
|
|
||||||
SERVICE_CHECK_DATE: Final = "check_date"
|
SERVICE_CHECK_DATE: Final = "check_date"
|
||||||
CHECK_DATE: Final = "check_date"
|
CHECK_DATE: Final = "check_date"
|
||||||
@@ -68,14 +60,10 @@ async def async_setup_entry(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
class IsWorkdaySensor(BinarySensorEntity):
|
class IsWorkdaySensor(BaseWorkdayEntity, BinarySensorEntity):
|
||||||
"""Implementation of a Workday sensor."""
|
"""Implementation of a Workday sensor."""
|
||||||
|
|
||||||
_attr_has_entity_name = True
|
|
||||||
_attr_name = None
|
_attr_name = None
|
||||||
_attr_translation_key = DOMAIN
|
|
||||||
_attr_should_poll = False
|
|
||||||
unsub: CALLBACK_TYPE | None = None
|
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@@ -87,87 +75,20 @@ class IsWorkdaySensor(BinarySensorEntity):
|
|||||||
entry_id: str,
|
entry_id: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Initialize the Workday sensor."""
|
"""Initialize the Workday sensor."""
|
||||||
self._obj_holidays = obj_holidays
|
super().__init__(
|
||||||
self._workdays = workdays
|
obj_holidays,
|
||||||
self._excludes = excludes
|
workdays,
|
||||||
self._days_offset = days_offset
|
excludes,
|
||||||
|
days_offset,
|
||||||
|
name,
|
||||||
|
entry_id,
|
||||||
|
)
|
||||||
self._attr_extra_state_attributes = {
|
self._attr_extra_state_attributes = {
|
||||||
CONF_WORKDAYS: workdays,
|
CONF_WORKDAYS: workdays,
|
||||||
CONF_EXCLUDES: excludes,
|
CONF_EXCLUDES: excludes,
|
||||||
CONF_OFFSET: days_offset,
|
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=python_holidays_version,
|
|
||||||
name=name,
|
|
||||||
)
|
|
||||||
|
|
||||||
def is_include(self, day: str, now: date) -> bool:
|
|
||||||
"""Check if given day is in the includes list."""
|
|
||||||
if day in self._workdays:
|
|
||||||
return True
|
|
||||||
if "holiday" in self._workdays and now in self._obj_holidays:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def is_exclude(self, day: str, now: date) -> bool:
|
|
||||||
"""Check if given day is in the excludes list."""
|
|
||||||
if day in self._excludes:
|
|
||||||
return True
|
|
||||||
if "holiday" in self._excludes and now in self._obj_holidays:
|
|
||||||
return True
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def get_next_interval(self, now: datetime) -> datetime:
|
|
||||||
"""Compute next time an update should occur."""
|
|
||||||
tomorrow = dt_util.as_local(now) + timedelta(days=1)
|
|
||||||
return dt_util.start_of_local_day(tomorrow)
|
|
||||||
|
|
||||||
def _update_state_and_setup_listener(self) -> None:
|
|
||||||
"""Update state and setup listener for next interval."""
|
|
||||||
now = dt_util.now()
|
|
||||||
self.update_data(now)
|
|
||||||
self.unsub = async_track_point_in_utc_time(
|
|
||||||
self.hass, self.point_in_time_listener, self.get_next_interval(now)
|
|
||||||
)
|
|
||||||
|
|
||||||
@callback
|
|
||||||
def point_in_time_listener(self, time_date: datetime) -> None:
|
|
||||||
"""Get the latest data and update state."""
|
|
||||||
self._update_state_and_setup_listener()
|
|
||||||
self.async_write_ha_state()
|
|
||||||
|
|
||||||
async def async_added_to_hass(self) -> None:
|
|
||||||
"""Set up first update."""
|
|
||||||
self._update_state_and_setup_listener()
|
|
||||||
|
|
||||||
def update_data(self, now: datetime) -> None:
|
def update_data(self, now: datetime) -> None:
|
||||||
"""Get date and look whether it is a holiday."""
|
"""Get date and look whether it is a holiday."""
|
||||||
self._attr_is_on = self.date_is_workday(now)
|
self._attr_is_on = self.date_is_workday(now)
|
||||||
|
|
||||||
def check_date(self, check_date: date) -> ServiceResponse:
|
|
||||||
"""Service to check if date is workday or not."""
|
|
||||||
return {"workday": self.date_is_workday(check_date)}
|
|
||||||
|
|
||||||
def date_is_workday(self, check_date: date) -> bool:
|
|
||||||
"""Check if date is workday."""
|
|
||||||
# Default is no workday
|
|
||||||
is_workday = False
|
|
||||||
|
|
||||||
# Get ISO day of the week (1 = Monday, 7 = Sunday)
|
|
||||||
adjusted_date = check_date + timedelta(days=self._days_offset)
|
|
||||||
day = adjusted_date.isoweekday() - 1
|
|
||||||
day_of_week = ALLOWED_DAYS[day]
|
|
||||||
|
|
||||||
if self.is_include(day_of_week, adjusted_date):
|
|
||||||
is_workday = True
|
|
||||||
|
|
||||||
if self.is_exclude(day_of_week, adjusted_date):
|
|
||||||
is_workday = False
|
|
||||||
|
|
||||||
return is_workday
|
|
||||||
|
115
homeassistant/components/workday/entity.py
Normal file
115
homeassistant/components/workday/entity.py
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
"""Base workday entity."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from abc import abstractmethod
|
||||||
|
from datetime import date, datetime, timedelta
|
||||||
|
|
||||||
|
from holidays import HolidayBase, __version__ as python_holidays_version
|
||||||
|
|
||||||
|
from homeassistant.core import CALLBACK_TYPE, ServiceResponse, callback
|
||||||
|
from homeassistant.helpers.device_registry import DeviceEntryType, DeviceInfo
|
||||||
|
from homeassistant.helpers.entity import Entity
|
||||||
|
from homeassistant.helpers.event import async_track_point_in_utc_time
|
||||||
|
from homeassistant.util import dt as dt_util
|
||||||
|
|
||||||
|
from .const import ALLOWED_DAYS, DOMAIN
|
||||||
|
|
||||||
|
|
||||||
|
class BaseWorkdayEntity(Entity):
|
||||||
|
"""Implementation of a base Workday entity."""
|
||||||
|
|
||||||
|
_attr_has_entity_name = True
|
||||||
|
_attr_translation_key = DOMAIN
|
||||||
|
_attr_should_poll = False
|
||||||
|
unsub: CALLBACK_TYPE | None = None
|
||||||
|
|
||||||
|
def __init__(
|
||||||
|
self,
|
||||||
|
obj_holidays: HolidayBase,
|
||||||
|
workdays: list[str],
|
||||||
|
excludes: list[str],
|
||||||
|
days_offset: int,
|
||||||
|
name: str,
|
||||||
|
entry_id: str,
|
||||||
|
) -> None:
|
||||||
|
"""Initialize the Workday entity."""
|
||||||
|
self._obj_holidays = obj_holidays
|
||||||
|
self._workdays = workdays
|
||||||
|
self._excludes = excludes
|
||||||
|
self._days_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=python_holidays_version,
|
||||||
|
name=name,
|
||||||
|
)
|
||||||
|
|
||||||
|
def is_include(self, day: str, now: date) -> bool:
|
||||||
|
"""Check if given day is in the includes list."""
|
||||||
|
if day in self._workdays:
|
||||||
|
return True
|
||||||
|
if "holiday" in self._workdays and now in self._obj_holidays:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def is_exclude(self, day: str, now: date) -> bool:
|
||||||
|
"""Check if given day is in the excludes list."""
|
||||||
|
if day in self._excludes:
|
||||||
|
return True
|
||||||
|
if "holiday" in self._excludes and now in self._obj_holidays:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_next_interval(self, now: datetime) -> datetime:
|
||||||
|
"""Compute next time an update should occur."""
|
||||||
|
tomorrow = dt_util.as_local(now) + timedelta(days=1)
|
||||||
|
return dt_util.start_of_local_day(tomorrow)
|
||||||
|
|
||||||
|
def _update_state_and_setup_listener(self) -> None:
|
||||||
|
"""Update state and setup listener for next interval."""
|
||||||
|
now = dt_util.now()
|
||||||
|
self.update_data(now)
|
||||||
|
self.unsub = async_track_point_in_utc_time(
|
||||||
|
self.hass, self.point_in_time_listener, self.get_next_interval(now)
|
||||||
|
)
|
||||||
|
|
||||||
|
@callback
|
||||||
|
def point_in_time_listener(self, time_date: datetime) -> None:
|
||||||
|
"""Get the latest data and update state."""
|
||||||
|
self._update_state_and_setup_listener()
|
||||||
|
self.async_write_ha_state()
|
||||||
|
|
||||||
|
async def async_added_to_hass(self) -> None:
|
||||||
|
"""Set up first update."""
|
||||||
|
self._update_state_and_setup_listener()
|
||||||
|
|
||||||
|
@abstractmethod
|
||||||
|
def update_data(self, now: datetime) -> None:
|
||||||
|
"""Update data."""
|
||||||
|
|
||||||
|
def check_date(self, check_date: date) -> ServiceResponse:
|
||||||
|
"""Service to check if date is workday or not."""
|
||||||
|
return {"workday": self.date_is_workday(check_date)}
|
||||||
|
|
||||||
|
def date_is_workday(self, check_date: date) -> bool:
|
||||||
|
"""Check if date is workday."""
|
||||||
|
# Default is no workday
|
||||||
|
is_workday = False
|
||||||
|
|
||||||
|
# Get ISO day of the week (1 = Monday, 7 = Sunday)
|
||||||
|
adjusted_date = check_date + timedelta(days=self._days_offset)
|
||||||
|
day = adjusted_date.isoweekday() - 1
|
||||||
|
day_of_week = ALLOWED_DAYS[day]
|
||||||
|
|
||||||
|
if self.is_include(day_of_week, adjusted_date):
|
||||||
|
is_workday = True
|
||||||
|
|
||||||
|
if self.is_exclude(day_of_week, adjusted_date):
|
||||||
|
is_workday = False
|
||||||
|
|
||||||
|
return is_workday
|
Reference in New Issue
Block a user