Fork garbage_collection avec décalage jours fériés FR
This commit is contained in:
@@ -0,0 +1,372 @@
|
||||
"""Component to integrate with garbage_colection."""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from datetime import timedelta
|
||||
from types import MappingProxyType
|
||||
from typing import Any, Dict
|
||||
|
||||
import homeassistant.helpers.config_validation as cv
|
||||
import homeassistant.util.dt as dt_util
|
||||
import voluptuous as vol
|
||||
from dateutil.relativedelta import relativedelta
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import ATTR_HIDDEN, CONF_ENTITIES, CONF_ENTITY_ID, WEEKDAYS
|
||||
from homeassistant.core import HomeAssistant, ServiceCall
|
||||
from homeassistant.helpers.typing import ConfigType
|
||||
|
||||
from . import const, helpers
|
||||
|
||||
MIN_TIME_BETWEEN_UPDATES = timedelta(seconds=30)
|
||||
|
||||
_LOGGER = logging.getLogger(__name__)
|
||||
|
||||
months = [m["value"] for m in const.MONTH_OPTIONS]
|
||||
frequencies = [f["value"] for f in const.FREQUENCY_OPTIONS]
|
||||
|
||||
SENSOR_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(const.CONF_FREQUENCY): vol.In(frequencies),
|
||||
vol.Optional(const.CONF_ICON_NORMAL): cv.icon,
|
||||
vol.Optional(const.CONF_ICON_TODAY): cv.icon,
|
||||
vol.Optional(const.CONF_ICON_TOMORROW): cv.icon,
|
||||
vol.Optional(const.CONF_EXPIRE_AFTER): helpers.time_text,
|
||||
vol.Optional(const.CONF_VERBOSE_STATE): cv.boolean,
|
||||
vol.Optional(ATTR_HIDDEN): cv.boolean,
|
||||
vol.Optional(const.CONF_MANUAL): cv.boolean,
|
||||
vol.Optional(const.CONF_DATE): helpers.month_day_text,
|
||||
vol.Optional(CONF_ENTITIES): cv.entity_ids,
|
||||
vol.Optional(const.CONF_COLLECTION_DAYS): vol.All(
|
||||
cv.ensure_list, [vol.In(WEEKDAYS)]
|
||||
),
|
||||
vol.Optional(const.CONF_FIRST_MONTH): vol.In(months),
|
||||
vol.Optional(const.CONF_LAST_MONTH): vol.In(months),
|
||||
vol.Optional(const.CONF_WEEKDAY_ORDER_NUMBER): vol.All(
|
||||
cv.ensure_list, [vol.All(vol.Coerce(int), vol.Range(min=1, max=5))]
|
||||
),
|
||||
vol.Optional(const.CONF_WEEK_ORDER_NUMBER): vol.All(
|
||||
cv.ensure_list, [vol.All(vol.Coerce(int), vol.Range(min=1, max=5))]
|
||||
),
|
||||
vol.Optional(const.CONF_PERIOD): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=1000)
|
||||
),
|
||||
vol.Optional(const.CONF_FIRST_WEEK): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=1, max=52)
|
||||
),
|
||||
vol.Optional(const.CONF_FIRST_DATE): cv.date,
|
||||
vol.Optional(const.CONF_VERBOSE_FORMAT): cv.string,
|
||||
vol.Optional(const.CONF_DATE_FORMAT): cv.string,
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
CONFIG_SCHEMA = vol.Schema(
|
||||
{
|
||||
const.DOMAIN: vol.Schema(
|
||||
{vol.Optional(const.CONF_SENSORS): vol.All(cv.ensure_list, [SENSOR_SCHEMA])}
|
||||
)
|
||||
},
|
||||
extra=vol.ALLOW_EXTRA,
|
||||
)
|
||||
|
||||
COLLECT_NOW_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Optional(const.ATTR_LAST_COLLECTION): cv.datetime,
|
||||
}
|
||||
)
|
||||
|
||||
UPDATE_STATE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
}
|
||||
)
|
||||
|
||||
ADD_REMOVE_DATE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Required(const.CONF_DATE): cv.date,
|
||||
}
|
||||
)
|
||||
|
||||
OFFSET_DATE_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(CONF_ENTITY_ID): vol.All(cv.ensure_list, [cv.string]),
|
||||
vol.Required(const.CONF_DATE): cv.date,
|
||||
vol.Required(const.CONF_OFFSET): vol.All(
|
||||
vol.Coerce(int), vol.Range(min=-31, max=31)
|
||||
),
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
async def async_setup(hass: HomeAssistant, _: ConfigType) -> bool:
|
||||
"""Set up platform - register services, inicialize data structure."""
|
||||
|
||||
async def handle_add_date(call: ServiceCall) -> None:
|
||||
"""Handle the add_date service call."""
|
||||
entity_ids = call.data.get(CONF_ENTITY_ID, [])
|
||||
collection_date = call.data.get(const.CONF_DATE)
|
||||
for entity_id in entity_ids:
|
||||
_LOGGER.debug("called add_date %s from %s", collection_date, entity_id)
|
||||
try:
|
||||
entity = hass.data[const.DOMAIN][const.SENSOR_PLATFORM][entity_id]
|
||||
await entity.add_date(collection_date)
|
||||
except KeyError as err:
|
||||
_LOGGER.error(
|
||||
"Failed adding date %s to %s (%s)",
|
||||
collection_date,
|
||||
entity_id,
|
||||
err,
|
||||
)
|
||||
|
||||
async def handle_remove_date(call: ServiceCall) -> None:
|
||||
"""Handle the remove_date service call."""
|
||||
entity_ids = call.data.get(CONF_ENTITY_ID, [])
|
||||
collection_date = call.data.get(const.CONF_DATE)
|
||||
for entity_id in entity_ids:
|
||||
_LOGGER.debug("called remove_date %s from %s", collection_date, entity_id)
|
||||
try:
|
||||
entity = hass.data[const.DOMAIN][const.SENSOR_PLATFORM][entity_id]
|
||||
await entity.remove_date(collection_date)
|
||||
except KeyError as err:
|
||||
_LOGGER.error(
|
||||
"Failed removing date %s from %s (%s)",
|
||||
collection_date,
|
||||
entity_id,
|
||||
err,
|
||||
)
|
||||
|
||||
async def handle_offset_date(call: ServiceCall) -> None:
|
||||
"""Handle the offset_date service call."""
|
||||
entity_ids = call.data.get(CONF_ENTITY_ID, [])
|
||||
offset = call.data.get(const.CONF_OFFSET)
|
||||
collection_date = call.data.get(const.CONF_DATE)
|
||||
for entity_id in entity_ids:
|
||||
_LOGGER.debug(
|
||||
"called offset_date %s by %d days for %s",
|
||||
collection_date,
|
||||
offset,
|
||||
entity_id,
|
||||
)
|
||||
try:
|
||||
new_date = collection_date + relativedelta(
|
||||
days=offset
|
||||
) # pyright: reportOptionalOperand=false
|
||||
entity = hass.data[const.DOMAIN][const.SENSOR_PLATFORM][entity_id]
|
||||
await asyncio.gather(
|
||||
entity.remove_date(collection_date), entity.add_date(new_date)
|
||||
)
|
||||
except (TypeError, KeyError) as err:
|
||||
_LOGGER.error("Failed ofsetting date for %s - %s", entity_id, err)
|
||||
break
|
||||
|
||||
async def handle_update_state(call: ServiceCall) -> None:
|
||||
"""Handle the update_state service call."""
|
||||
entity_ids = call.data.get(CONF_ENTITY_ID, [])
|
||||
for entity_id in entity_ids:
|
||||
_LOGGER.debug("called update_state for %s", entity_id)
|
||||
try:
|
||||
entity = hass.data[const.DOMAIN][const.SENSOR_PLATFORM][entity_id]
|
||||
entity.update_state()
|
||||
except KeyError as err:
|
||||
_LOGGER.error("Failed updating state for %s - %s", entity_id, err)
|
||||
|
||||
async def handle_collect_garbage(call: ServiceCall) -> None:
|
||||
"""Handle the collect_garbage service call."""
|
||||
entity_ids = call.data.get(CONF_ENTITY_ID, [])
|
||||
last_collection = call.data.get(const.ATTR_LAST_COLLECTION, helpers.now())
|
||||
for entity_id in entity_ids:
|
||||
_LOGGER.debug("called collect_garbage for %s", entity_id)
|
||||
try:
|
||||
entity = hass.data[const.DOMAIN][const.SENSOR_PLATFORM][entity_id]
|
||||
entity.last_collection = dt_util.as_local(last_collection)
|
||||
entity.update_state()
|
||||
except KeyError as err:
|
||||
_LOGGER.error(
|
||||
"Failed setting last collection for %s - %s", entity_id, err
|
||||
)
|
||||
|
||||
hass.data.setdefault(const.DOMAIN, {})
|
||||
hass.data[const.DOMAIN].setdefault(const.SENSOR_PLATFORM, {})
|
||||
hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
"collect_garbage",
|
||||
handle_collect_garbage,
|
||||
schema=COLLECT_NOW_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
"update_state",
|
||||
handle_update_state,
|
||||
schema=UPDATE_STATE_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
const.DOMAIN, "add_date", handle_add_date, schema=ADD_REMOVE_DATE_SCHEMA
|
||||
)
|
||||
hass.services.async_register(
|
||||
const.DOMAIN,
|
||||
"remove_date",
|
||||
handle_remove_date,
|
||||
schema=ADD_REMOVE_DATE_SCHEMA,
|
||||
)
|
||||
hass.services.async_register(
|
||||
const.DOMAIN, "offset_date", handle_offset_date, schema=OFFSET_DATE_SCHEMA
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Set up this integration using UI."""
|
||||
_LOGGER.debug(
|
||||
"Setting %s (%s) from ConfigFlow",
|
||||
config_entry.title,
|
||||
config_entry.options[const.CONF_FREQUENCY],
|
||||
)
|
||||
config_entry.add_update_listener(update_listener)
|
||||
# Add sensor
|
||||
hass.async_create_task(
|
||||
hass.config_entries.async_forward_entry_setup(
|
||||
config_entry, const.SENSOR_PLATFORM
|
||||
)
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def async_remove_entry(hass: HomeAssistant, config_entry: ConfigEntry) -> None:
|
||||
"""Handle removal of an entry."""
|
||||
try:
|
||||
await hass.config_entries.async_forward_entry_unload(
|
||||
config_entry, const.SENSOR_PLATFORM
|
||||
)
|
||||
_LOGGER.info(
|
||||
"Successfully removed sensor from the garbage_collection integration"
|
||||
)
|
||||
except ValueError:
|
||||
pass
|
||||
|
||||
|
||||
async def async_migrate_entry(_: HomeAssistant, config_entry: ConfigEntry) -> bool:
|
||||
"""Migrate old entry."""
|
||||
_LOGGER.info(
|
||||
"Migrating %s from version %s", config_entry.title, config_entry.version
|
||||
)
|
||||
new_data: Dict[str, Any] = {**config_entry.data}
|
||||
new_options: Dict[str, Any] = {**config_entry.options}
|
||||
removed_data: Dict[str, Any] = {}
|
||||
removed_options: Dict[str, Any] = {}
|
||||
_LOGGER.debug("new_data %s", new_data)
|
||||
_LOGGER.debug("new_options %s", new_options)
|
||||
if config_entry.version == 1:
|
||||
to_remove = [
|
||||
"offset",
|
||||
"move_country_holidays",
|
||||
"holiday_in_week_move",
|
||||
"holiday_pop_named",
|
||||
"holiday_move_offset",
|
||||
"prov",
|
||||
"state",
|
||||
"observed",
|
||||
"exclude_dates",
|
||||
"include_dates",
|
||||
]
|
||||
for remove in to_remove:
|
||||
if remove in new_data:
|
||||
removed_data[remove] = new_data[remove]
|
||||
del new_data[remove]
|
||||
if remove in new_options:
|
||||
removed_options[remove] = new_options[remove]
|
||||
del new_options[remove]
|
||||
if new_data.get(const.CONF_FREQUENCY) in const.MONTHLY_FREQUENCY:
|
||||
if const.CONF_WEEK_ORDER_NUMBER in new_data:
|
||||
new_data[const.CONF_WEEKDAY_ORDER_NUMBER] = new_data[
|
||||
const.CONF_WEEK_ORDER_NUMBER
|
||||
]
|
||||
new_data[const.CONF_FORCE_WEEK_NUMBERS] = True
|
||||
del new_data[const.CONF_WEEK_ORDER_NUMBER]
|
||||
else:
|
||||
new_data[const.CONF_FORCE_WEEK_NUMBERS] = False
|
||||
_LOGGER.info("Updated data config for week_order_number")
|
||||
if new_options.get(const.CONF_FREQUENCY) in const.MONTHLY_FREQUENCY:
|
||||
if const.CONF_WEEK_ORDER_NUMBER in new_options:
|
||||
new_options[const.CONF_WEEKDAY_ORDER_NUMBER] = new_options[
|
||||
const.CONF_WEEK_ORDER_NUMBER
|
||||
]
|
||||
new_options[const.CONF_FORCE_WEEK_NUMBERS] = True
|
||||
del new_options[const.CONF_WEEK_ORDER_NUMBER]
|
||||
_LOGGER.info("Updated options config for week_order_number")
|
||||
else:
|
||||
new_options[const.CONF_FORCE_WEEK_NUMBERS] = False
|
||||
if config_entry.version <= 4:
|
||||
if const.CONF_WEEKDAY_ORDER_NUMBER in new_data:
|
||||
new_data[const.CONF_WEEKDAY_ORDER_NUMBER] = list(
|
||||
map(str, new_data[const.CONF_WEEKDAY_ORDER_NUMBER])
|
||||
)
|
||||
if const.CONF_WEEKDAY_ORDER_NUMBER in new_options:
|
||||
new_options[const.CONF_WEEKDAY_ORDER_NUMBER] = list(
|
||||
map(str, new_options[const.CONF_WEEKDAY_ORDER_NUMBER])
|
||||
)
|
||||
if config_entry.version <= 5:
|
||||
for conf in [
|
||||
const.CONF_FREQUENCY,
|
||||
const.CONF_ICON_NORMAL,
|
||||
const.CONF_ICON_TODAY,
|
||||
const.CONF_ICON_TOMORROW,
|
||||
const.CONF_MANUAL,
|
||||
const.CONF_OFFSET,
|
||||
const.CONF_EXPIRE_AFTER,
|
||||
const.CONF_VERBOSE_STATE,
|
||||
const.CONF_FIRST_MONTH,
|
||||
const.CONF_LAST_MONTH,
|
||||
const.CONF_COLLECTION_DAYS,
|
||||
const.CONF_WEEKDAY_ORDER_NUMBER,
|
||||
const.CONF_FORCE_WEEK_NUMBERS,
|
||||
const.CONF_WEEK_ORDER_NUMBER,
|
||||
const.CONF_DATE,
|
||||
const.CONF_PERIOD,
|
||||
const.CONF_FIRST_WEEK,
|
||||
const.CONF_FIRST_DATE,
|
||||
const.CONF_SENSORS,
|
||||
const.CONF_VERBOSE_FORMAT,
|
||||
const.CONF_DATE_FORMAT,
|
||||
]:
|
||||
if conf in new_data:
|
||||
new_options[conf] = new_data.get(conf)
|
||||
del new_data[conf]
|
||||
if (
|
||||
const.CONF_EXPIRE_AFTER in new_options
|
||||
and len(new_options[const.CONF_EXPIRE_AFTER]) == 5
|
||||
):
|
||||
new_options[const.CONF_EXPIRE_AFTER] = (
|
||||
new_options[const.CONF_EXPIRE_AFTER] + ":00"
|
||||
)
|
||||
config_entry.version = const.CONFIG_VERSION
|
||||
config_entry.data = MappingProxyType({**new_data})
|
||||
config_entry.options = MappingProxyType({**new_options})
|
||||
if removed_data:
|
||||
_LOGGER.error(
|
||||
"Removed data config %s. "
|
||||
"Please check the documentation how to configure the functionality.",
|
||||
removed_data,
|
||||
)
|
||||
if removed_options:
|
||||
_LOGGER.error(
|
||||
"Removed options config %s. "
|
||||
"Please check the documentation how to configure the functionality.",
|
||||
removed_options,
|
||||
)
|
||||
_LOGGER.info(
|
||||
"%s migration to version %s successful",
|
||||
config_entry.title,
|
||||
config_entry.version,
|
||||
)
|
||||
return True
|
||||
|
||||
|
||||
async def update_listener(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Update listener - to re-create device after options update."""
|
||||
await hass.config_entries.async_forward_entry_unload(entry, const.SENSOR_PLATFORM)
|
||||
hass.async_add_job(
|
||||
hass.config_entries.async_forward_entry_setup(entry, const.SENSOR_PLATFORM)
|
||||
)
|
||||
Reference in New Issue
Block a user