Fork garbage_collection avec décalage jours fériés FR

This commit is contained in:
2026-06-17 11:16:24 +02:00
commit 162483ba3c
43 changed files with 4120 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
"""Needed for pytest."""
@@ -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)
)
@@ -0,0 +1,151 @@
"""Garbage collection calendar."""
from __future__ import annotations
from datetime import datetime, timedelta
from homeassistant.components.calendar import CalendarEntity, CalendarEvent
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.util import Throttle
from .const import CALENDAR_NAME, CALENDAR_PLATFORM, DOMAIN, SENSOR_PLATFORM
MIN_TIME_BETWEEN_UPDATES = timedelta(minutes=1)
async def async_setup_entry(
_: HomeAssistant, config_entry: ConfigEntry, async_add_entities: AddEntitiesCallback
) -> None:
# pylint: disable=unused-argument
"""Add calendar entity to HA."""
async_add_entities([GarbageCollectionCalendar()], True)
class GarbageCollectionCalendar(CalendarEntity):
"""The garbage collection calendar class."""
instances = False
def __init__(self) -> None:
"""Create empty calendar."""
self._cal_data: dict = {}
self._attr_name = CALENDAR_NAME
GarbageCollectionCalendar.instances = True
@property
def event(self) -> CalendarEvent | None:
"""Return the next upcoming event."""
return self.hass.data[DOMAIN][CALENDAR_PLATFORM].event
@property
def name(self) -> str | None:
"""Return the name of the entity."""
return self._attr_name
async def async_update(self) -> None:
"""Update all calendars."""
await self.hass.data[DOMAIN][CALENDAR_PLATFORM].async_update()
async def async_get_events(
self, hass: HomeAssistant, start_date: datetime, end_date: datetime
) -> list[CalendarEvent]:
"""Get all events in a specific time frame."""
return await self.hass.data[DOMAIN][CALENDAR_PLATFORM].async_get_events(
hass, start_date, end_date
)
@property
def extra_state_attributes(self) -> dict | None:
"""Return the device state attributes."""
if self.hass.data[DOMAIN][CALENDAR_PLATFORM].event is None:
# No tasks, we don't need to show anything.
return None
return {}
class EntitiesCalendarData:
"""Class used by the Entities Calendar class to hold all entity events."""
__slots__ = "_hass", "event", "entities", "_throttle"
def __init__(self, hass: HomeAssistant) -> None:
"""Initialize an Entities Calendar Data."""
self._hass = hass
self.event: CalendarEvent | None = None
self.entities: list[str] = []
def add_entity(self, entity_id: str) -> None:
"""Append entity ID to the calendar."""
if entity_id not in self.entities:
self.entities.append(entity_id)
def remove_entity(self, entity_id: str) -> None:
"""Remove entity ID from the calendar."""
if entity_id in self.entities:
self.entities.remove(entity_id)
async def async_get_events(
self, hass: HomeAssistant, start_datetime: datetime, end_datetime: datetime
) -> list[CalendarEvent]:
"""Get all tasks in a specific time frame."""
events: list[CalendarEvent] = []
if SENSOR_PLATFORM not in hass.data[DOMAIN]:
return events
start_date = start_datetime.date()
end_date = end_datetime.date()
for entity in self.entities:
if (
entity not in hass.data[DOMAIN][SENSOR_PLATFORM]
or hass.data[DOMAIN][SENSOR_PLATFORM][entity].hidden
):
continue
garbage_collection = hass.data[DOMAIN][SENSOR_PLATFORM][entity]
start = garbage_collection.get_next_date(start_date, True)
while start is not None and start_date <= start <= end_date:
try:
end = start + timedelta(days=1)
except TypeError:
end = start
name = (
garbage_collection.name
if garbage_collection.name is not None
else "Unknown"
)
if garbage_collection.expire_after is None:
event = CalendarEvent(
summary=name,
start=start,
end=end,
)
else:
event = CalendarEvent(
summary=name,
start=datetime.combine(start, datetime.min.time()),
end=datetime.combine(start, garbage_collection.expire_after),
)
events.append(event)
start = garbage_collection.get_next_date(
start + timedelta(days=1), True
)
return events
@Throttle(MIN_TIME_BETWEEN_UPDATES)
async def async_update(self) -> None:
"""Get the latest data."""
next_dates = {}
for entity in self.entities:
if self._hass.data[DOMAIN][SENSOR_PLATFORM][entity].next_date is not None:
next_dates[entity] = self._hass.data[DOMAIN][SENSOR_PLATFORM][
entity
].next_date
if len(next_dates) > 0:
entity_id = min(next_dates.keys(), key=(lambda k: next_dates[k]))
start = next_dates[entity_id]
end = start + timedelta(days=1)
name = self._hass.data[DOMAIN][SENSOR_PLATFORM][entity_id].name
self.event = CalendarEvent(
summary=name,
start=start,
end=end,
)
@@ -0,0 +1,281 @@
"""Adds config flow for GarbageCollection."""
from __future__ import annotations
import logging
# import uuid
from collections.abc import Mapping
from typing import Any, Dict, cast
import voluptuous as vol
from homeassistant.const import ATTR_HIDDEN, CONF_ENTITIES, CONF_NAME
from homeassistant.core import callback
from homeassistant.helpers import selector
from homeassistant.helpers.schema_config_entry_flow import (
SchemaConfigFlowHandler,
SchemaFlowError,
SchemaFlowFormStep,
SchemaFlowMenuStep,
SchemaOptionsFlowHandler,
)
from . import const, helpers
_LOGGER = logging.getLogger(__name__)
async def _validate_config(
_: SchemaConfigFlowHandler | SchemaOptionsFlowHandler, data: Any
) -> Any:
"""Validate config."""
if const.CONF_DATE in data:
try:
helpers.month_day_text(data[const.CONF_DATE])
except vol.Invalid as exc:
raise SchemaFlowError("month_day") from exc
return data
def required(
key: str, options: Dict[str, Any], default: Any | None = None
) -> vol.Required:
"""Return vol.Required."""
if isinstance(options, dict) and key in options:
suggested_value = options[key]
elif default is not None:
suggested_value = default
else:
return vol.Required(key)
return vol.Required(key, description={"suggested_value": suggested_value})
def optional(
key: str, options: Dict[str, Any], default: Any | None = None
) -> vol.Optional:
"""Return vol.Optional."""
if isinstance(options, dict) and key in options:
suggested_value = options[key]
elif default is not None:
suggested_value = default
else:
return vol.Optional(key)
return vol.Optional(key, description={"suggested_value": suggested_value})
async def general_config_schema(
handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler,
) -> vol.Schema:
"""Generate config schema."""
return vol.Schema(
{
optional(CONF_NAME, handler.options): selector.TextSelector(),
required(
const.CONF_FREQUENCY, handler.options, const.DEFAULT_FREQUENCY
): selector.SelectSelector(
selector.SelectSelectorConfig(options=const.FREQUENCY_OPTIONS)
),
optional(
const.CONF_ICON_NORMAL, handler.options, const.DEFAULT_ICON_NORMAL
): selector.IconSelector(),
optional(
const.CONF_ICON_TODAY, handler.options, const.DEFAULT_ICON_TODAY
): selector.IconSelector(),
optional(
const.CONF_ICON_TOMORROW, handler.options, const.DEFAULT_ICON_TOMORROW
): selector.IconSelector(),
optional(const.CONF_EXPIRE_AFTER, handler.options): selector.TimeSelector(),
optional(
const.CONF_VERBOSE_STATE, handler.options, const.DEFAULT_VERBOSE_STATE
): bool,
optional(ATTR_HIDDEN, handler.options, False): bool,
optional(const.CONF_MANUAL, handler.options, False): bool,
optional(
const.CONF_MOVE_COUNTRY_HOLIDAYS,
handler.options,
const.DEFAULT_HOLIDAY_IN_WEEK_MOVE,
): bool,
optional(
const.CONF_HOLIDAY_COUNTRY,
handler.options,
const.DEFAULT_HOLIDAY_COUNTRY,
): selector.TextSelector(),
}
)
async def general_options_schema(
handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler,
) -> vol.Schema:
"""Generate options schema."""
return vol.Schema(
{
required(
const.CONF_FREQUENCY, handler.options, const.DEFAULT_FREQUENCY
): selector.SelectSelector(
selector.SelectSelectorConfig(options=const.FREQUENCY_OPTIONS)
),
optional(
const.CONF_ICON_NORMAL, handler.options, const.DEFAULT_ICON_NORMAL
): selector.IconSelector(),
optional(
const.CONF_ICON_TODAY, handler.options, const.DEFAULT_ICON_TODAY
): selector.IconSelector(),
optional(
const.CONF_ICON_TOMORROW, handler.options, const.DEFAULT_ICON_TOMORROW
): selector.IconSelector(),
optional(const.CONF_EXPIRE_AFTER, handler.options): selector.TimeSelector(),
optional(
const.CONF_VERBOSE_STATE, handler.options, const.DEFAULT_VERBOSE_STATE
): bool,
optional(ATTR_HIDDEN, handler.options, False): bool,
optional(const.CONF_MANUAL, handler.options, False): bool,
optional(
const.CONF_MOVE_COUNTRY_HOLIDAYS,
handler.options,
const.DEFAULT_HOLIDAY_IN_WEEK_MOVE,
): bool,
optional(
const.CONF_HOLIDAY_COUNTRY,
handler.options,
const.DEFAULT_HOLIDAY_COUNTRY,
): selector.TextSelector(),
}
)
async def detail_config_schema(
handler: SchemaConfigFlowHandler | SchemaOptionsFlowHandler,
) -> vol.Schema:
"""Generate options schema."""
options_schema: Dict[vol.Optional | vol.Required, Any] = {}
if handler.options[const.CONF_FREQUENCY] in const.ANNUAL_FREQUENCY:
# "annual"
options_schema[
required(const.CONF_DATE, handler.options)
] = selector.TextSelector()
elif handler.options[const.CONF_FREQUENCY] in const.GROUP_FREQUENCY:
# "group"
options_schema[
required(CONF_ENTITIES, handler.options)
] = selector.EntitySelector(
selector.EntitySelectorConfig(
domain="sensor", integration=const.DOMAIN, multiple=True
),
)
elif handler.options[const.CONF_FREQUENCY] not in const.BLANK_FREQUENCY:
# everything else except "blank" and every-n-days
if handler.options[const.CONF_FREQUENCY] not in const.DAILY_FREQUENCY:
options_schema[
required(const.CONF_COLLECTION_DAYS, handler.options)
] = selector.SelectSelector(
selector.SelectSelectorConfig(
options=const.WEEKDAY_OPTIONS,
multiple=True,
mode=selector.SelectSelectorMode.LIST,
)
)
# everything else except "blank"
options_schema[
optional(const.CONF_FIRST_MONTH, handler.options, const.DEFAULT_FIRST_MONTH)
] = selector.SelectSelector(
selector.SelectSelectorConfig(options=const.MONTH_OPTIONS)
)
options_schema[
optional(const.CONF_LAST_MONTH, handler.options, const.DEFAULT_LAST_MONTH)
] = selector.SelectSelector(
selector.SelectSelectorConfig(options=const.MONTH_OPTIONS)
)
if handler.options[const.CONF_FREQUENCY] in const.MONTHLY_FREQUENCY:
# "monthly"
options_schema[
optional(const.CONF_WEEKDAY_ORDER_NUMBER, handler.options)
] = selector.SelectSelector(
selector.SelectSelectorConfig(
options=const.ORDER_OPTIONS,
multiple=True,
mode=selector.SelectSelectorMode.LIST,
)
)
options_schema[
optional(const.CONF_FORCE_WEEK_NUMBERS, handler.options)
] = selector.BooleanSelector()
if handler.options[const.CONF_FREQUENCY] in const.WEEKLY_DAILY_MONTHLY:
# "every-n-weeks", "every-n-days", "monthly"
uom = {"every-n-weeks": "weeks", "every-n-days": "days", "monthly": "month"}
options_schema[
required(const.CONF_PERIOD, handler.options)
] = selector.NumberSelector(
selector.NumberSelectorConfig(
min=1,
max=1000,
mode=selector.NumberSelectorMode.BOX,
unit_of_measurement=uom[handler.options[const.CONF_FREQUENCY]],
)
)
if handler.options[const.CONF_FREQUENCY] in const.WEEKLY_FREQUENCY_X:
# every-n-weeks
options_schema[
required(
const.CONF_FIRST_WEEK, handler.options, const.DEFAULT_FIRST_WEEK
)
] = selector.NumberSelector(
selector.NumberSelectorConfig(
min=1,
max=52,
mode=selector.NumberSelectorMode.BOX,
unit_of_measurement="weeks",
)
)
if handler.options[const.CONF_FREQUENCY] in const.DAILY_FREQUENCY:
# every-n-days
options_schema[
required(const.CONF_FIRST_DATE, handler.options, helpers.now().date())
] = selector.DateSelector()
if handler.options.get(const.CONF_VERBOSE_STATE, False):
# "verbose_state"
options_schema[
required(
const.CONF_VERBOSE_FORMAT, handler.options, const.DEFAULT_VERBOSE_FORMAT
)
] = selector.TextSelector()
options_schema[
required(const.CONF_DATE_FORMAT, handler.options, const.DEFAULT_DATE_FORMAT)
] = selector.TextSelector()
return vol.Schema(options_schema)
async def choose_details_step(_: dict[str, Any]) -> str:
"""Return next step_id for options flow."""
return "detail"
CONFIG_FLOW: Dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = {
"user": SchemaFlowFormStep(general_config_schema, next_step=choose_details_step),
"detail": SchemaFlowFormStep(
detail_config_schema, validate_user_input=_validate_config
),
}
OPTIONS_FLOW: Dict[str, SchemaFlowFormStep | SchemaFlowMenuStep] = {
"init": SchemaFlowFormStep(general_options_schema, next_step=choose_details_step),
"detail": SchemaFlowFormStep(
detail_config_schema, validate_user_input=_validate_config
),
}
# mypy: ignore-errors
class GarbageCollectionConfigFlowHandler(SchemaConfigFlowHandler, domain=const.DOMAIN):
"""Handle a config or options flow for GarbageCollection."""
config_flow = CONFIG_FLOW
options_flow = OPTIONS_FLOW
VERSION = const.CONFIG_VERSION
@callback
def async_config_entry_title(self, options: Mapping[str, Any]) -> str:
"""Return config entry title.
The options parameter contains config entry options, which is the union of user
input from the config flow steps.
"""
return cast(str, options["name"]) if "name" in options else ""
@@ -0,0 +1,144 @@
"""Define constants used in garbage_collection."""
from homeassistant.helpers import selector
# Constants for garbage_collection.
# Base component constants
DOMAIN = "garbage_collection"
CALENDAR_NAME = "Garbage Collection"
SENSOR_PLATFORM = "sensor"
CALENDAR_PLATFORM = "calendar"
ATTRIBUTION = "Data from this is provided by garbage_collection."
CONFIG_VERSION = 6
ATTR_NEXT_DATE = "next_date"
ATTR_DAYS = "days"
ATTR_LAST_COLLECTION = "last_collection"
ATTR_LAST_UPDATED = "last_updated"
# Device classes
BINARY_SENSOR_DEVICE_CLASS = "connectivity"
DEVICE_CLASS = "garbage_collection__schedule"
# Configuration
CONF_SENSOR = "sensor"
CONF_ENABLED = "enabled"
CONF_FREQUENCY = "frequency"
CONF_MANUAL = "manual_update"
CONF_ICON_NORMAL = "icon_normal"
CONF_ICON_TODAY = "icon_today"
CONF_ICON_TOMORROW = "icon_tomorrow"
CONF_OFFSET = "offset"
CONF_EXPIRE_AFTER = "expire_after"
CONF_VERBOSE_STATE = "verbose_state"
CONF_FIRST_MONTH = "first_month"
CONF_LAST_MONTH = "last_month"
CONF_COLLECTION_DAYS = "collection_days"
CONF_WEEKDAY_ORDER_NUMBER = "weekday_order_number"
CONF_FORCE_WEEK_NUMBERS = "force_week_order_numbers"
CONF_WEEK_ORDER_NUMBER = "week_order_number" # Obsolete
CONF_DATE = "date"
CONF_PERIOD = "period"
CONF_FIRST_WEEK = "first_week"
CONF_FIRST_DATE = "first_date"
CONF_SENSORS = "sensors"
CONF_VERBOSE_FORMAT = "verbose_format"
CONF_DATE_FORMAT = "date_format"
CONF_MOVE_COUNTRY_HOLIDAYS = "move_country_holidays"
CONF_HOLIDAY_COUNTRY = "holiday_country"
# Defaults
DEFAULT_NAME = DOMAIN
DEFAULT_FIRST_MONTH = "jan"
DEFAULT_LAST_MONTH = "dec"
DEFAULT_FREQUENCY = "weekly"
DEFAULT_PERIOD = 1
DEFAULT_FIRST_WEEK = 1
DEFAULT_VERBOSE_STATE = False
DEFAULT_HOLIDAY_IN_WEEK_MOVE = False
DEFAULT_HOLIDAY_COUNTRY = "FR"
DEFAULT_DATE_FORMAT = "%d-%b-%Y"
DEFAULT_VERBOSE_FORMAT = "on {date}, in {days} days"
# Icons
DEFAULT_ICON_NORMAL = "mdi:trash-can"
DEFAULT_ICON_TODAY = "mdi:delete-restore"
DEFAULT_ICON_TOMORROW = "mdi:delete-circle"
ICON = DEFAULT_ICON_NORMAL
# States
STATE_TODAY = "today"
STATE_TOMORROW = "tomorrow"
FREQUENCY_OPTIONS = [
selector.SelectOptionDict(value="weekly", label="weekly"),
selector.SelectOptionDict(value="even-weeks", label="even-weeks"),
selector.SelectOptionDict(value="odd-weeks", label="odd-weeks"),
selector.SelectOptionDict(value="every-n-weeks", label="every-n-weeks"),
selector.SelectOptionDict(value="every-n-days", label="every-n-days"),
selector.SelectOptionDict(value="monthly", label="monthly"),
selector.SelectOptionDict(value="annual", label="annual"),
selector.SelectOptionDict(value="blank", label="blank"),
selector.SelectOptionDict(value="group", label="group"),
]
WEEKLY_FREQUENCY = ["weekly", "even-weeks", "odd-weeks"]
EXCEPT_ANNUAL_GROUP = [
"weekly",
"even-weeks",
"odd-weeks",
"every-n-weeks",
"every-n-days",
"monthly",
"blank",
]
EXCEPT_ANNUAL_GROUP_BLANK = [
"weekly",
"even-weeks",
"odd-weeks",
"every-n-weeks",
"every-n-days",
"monthly",
]
WEEKLY_DAILY_MONTHLY = ["every-n-weeks", "every-n-days", "monthly"]
WEEKLY_FREQUENCY_X = ["every-n-weeks"]
DAILY_FREQUENCY = ["every-n-days"]
DAILY_BLANK_FREQUENCY = ["blank", "every-n-days"]
MONTHLY_FREQUENCY = ["monthly"]
ANNUAL_GROUP_FREQUENCY = ["annual", "group"]
ANNUAL_FREQUENCY = ["annual"]
GROUP_FREQUENCY = ["group"]
BLANK_FREQUENCY = ["blank"]
WEEKDAY_OPTIONS = [
selector.SelectOptionDict(value="mon", label="Monday"),
selector.SelectOptionDict(value="tue", label="Tuesday"),
selector.SelectOptionDict(value="wed", label="Wednesday"),
selector.SelectOptionDict(value="thu", label="Thursday"),
selector.SelectOptionDict(value="fri", label="Friday"),
selector.SelectOptionDict(value="sat", label="Saturday"),
selector.SelectOptionDict(value="sun", label="Sunday"),
]
MONTH_OPTIONS = [
selector.SelectOptionDict(value="jan", label="January"),
selector.SelectOptionDict(value="feb", label="February"),
selector.SelectOptionDict(value="mar", label="March"),
selector.SelectOptionDict(value="apr", label="April"),
selector.SelectOptionDict(value="may", label="May"),
selector.SelectOptionDict(value="jun", label="June"),
selector.SelectOptionDict(value="jul", label="July"),
selector.SelectOptionDict(value="aug", label="August"),
selector.SelectOptionDict(value="sep", label="September"),
selector.SelectOptionDict(value="oct", label="October"),
selector.SelectOptionDict(value="nov", label="November"),
selector.SelectOptionDict(value="dec", label="December"),
]
ORDER_OPTIONS = [
selector.SelectOptionDict(value="1", label="1st"),
selector.SelectOptionDict(value="2", label="2nd"),
selector.SelectOptionDict(value="3", label="3rd"),
selector.SelectOptionDict(value="4", label="4th"),
selector.SelectOptionDict(value="5", label="5th"),
]
@@ -0,0 +1,29 @@
"""Diagnostics support for Garbage Collection."""
from __future__ import annotations
from typing import Any, Dict
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
from . import const
async def async_get_config_entry_diagnostics(
hass: HomeAssistant,
entry: ConfigEntry,
) -> Dict[str, Any]:
"""Return diagnostics for a config entry."""
entities = hass.data[const.DOMAIN][const.SENSOR_PLATFORM]
entity_data = [
entities[entity]
for entity in entities
if entities[entity].unique_id == entry.data["unique_id"]
][0]
data = {
"entity_id": entity_data.entity_id,
"state": entity_data.state,
"attributes": entity_data.extra_state_attributes,
"config_entry": entry.as_dict(),
}
return data
@@ -0,0 +1,67 @@
"""Set of functions to handle date and text conversion."""
from __future__ import annotations
from datetime import date, datetime
from typing import Any
import homeassistant.util.dt as dt_util
import voluptuous as vol
from dateutil.parser import ParserError, parse
def now() -> datetime:
"""Return current date and time. Needed for testing."""
return dt_util.now()
def to_date(day: Any) -> date:
"""Convert datetime or text to date, if not already datetime.
Used for the first date for every_n_days (configured as text)
"""
if day is None:
raise ValueError
if isinstance(day, date):
return day
if isinstance(day, datetime):
return day.date()
return date.fromisoformat(day)
def parse_datetime(text: str) -> datetime | None:
"""Parse text to datetime object."""
try:
return parse(text)
except (ParserError, TypeError):
return None
def dates_to_texts(dates: list[date]) -> list[str]:
"""Convert list of dates to texts."""
converted: list[str] = []
for record in dates:
try:
converted.append(record.isoformat())
except ValueError:
continue
return converted
def time_text(value: Any) -> str:
"""Have to store time as text - datetime is not JSON serialisable."""
if value is None or value == "":
return ""
try:
return datetime.strptime(value, "%H:%M").time().strftime("%H:%M")
except ValueError as error:
raise vol.Invalid(f"Invalid date: {value}") from error
def month_day_text(value: Any) -> str:
"""Validate format month/day."""
if value is None or value == "":
return ""
try:
return datetime.strptime(value, "%m/%d").date().strftime("%m/%d")
except ValueError as error:
raise vol.Invalid(f"Invalid date: {value}") from error
@@ -0,0 +1,18 @@
{
"domain": "garbage_collection",
"name": "Garbage Collection",
"codeowners": [
"@bruxy70"
],
"config_flow": true,
"dependencies": [],
"documentation": "https://github.com/bruxy70/Garbage-Collection/",
"integration_type": "helper",
"iot_class": "calculated",
"issue_tracker": "https://github.com/bruxy70/Garbage-Collection/issues",
"requirements": [
"python-dateutil>=2.8.2",
"holidays>=0.40"
],
"version": "3.21-morgane-fr"
}
@@ -0,0 +1,840 @@
"""Sensor platform for garbage_collection."""
from __future__ import annotations
import logging
from datetime import date, datetime, time, timedelta
from typing import Any, Dict, Generator
from dateutil.relativedelta import relativedelta
import holidays
from homeassistant.config_entries import ConfigEntry
from homeassistant.const import (
ATTR_DEVICE_CLASS,
ATTR_HIDDEN,
CONF_ENTITIES,
CONF_NAME,
WEEKDAYS,
)
from homeassistant.core import HomeAssistant
from homeassistant.helpers import device_registry as dr
from homeassistant.helpers.entity import DeviceInfo
from homeassistant.helpers.entity_platform import AddEntitiesCallback
from homeassistant.helpers.restore_state import RestoreEntity
from . import const, helpers
from .calendar import EntitiesCalendarData
_LOGGER = logging.getLogger(__name__)
SCAN_INTERVAL = timedelta(seconds=10)
THROTTLE_INTERVAL = timedelta(seconds=60)
async def async_setup_entry(
_: HomeAssistant, config_entry: ConfigEntry, async_add_devices: AddEntitiesCallback
) -> None:
"""Create garbage collection entities defined in config_flow and add them to HA."""
frequency = config_entry.options.get(const.CONF_FREQUENCY)
name = (
config_entry.title
if config_entry.title is not None
else config_entry.data.get(CONF_NAME)
)
_frequency_function = {
"weekly": WeeklyCollection,
"even-weeks": WeeklyCollection,
"odd-weeks": WeeklyCollection,
"every-n-weeks": WeeklyCollection,
"every-n-days": DailyCollection,
"monthly": MonthlyCollection,
"annual": AnnualCollection,
"group": GroupCollection,
"blank": BlankCollection,
}
if frequency in _frequency_function:
add_devices = _frequency_function[frequency]
async_add_devices([add_devices(config_entry)], True)
else:
_LOGGER.error("(%s) Unknown frequency %s", name, frequency)
raise ValueError
class GarbageCollection(RestoreEntity):
"""GarbageCollection Sensor class."""
__slots__ = (
"_attr_icon",
"_attr_name",
"_attr_state",
"_collection_dates",
"_date_format",
"_days",
"_first_month",
"_hidden",
"_holiday_country",
"_holidays_calendar",
"_icon_normal",
"_icon_today",
"_icon_tomorrow",
"_last_month",
"_last_updated",
"_manual",
"_next_date",
"_verbose_format",
"_verbose_state",
"config_entry",
"expire_after",
"last_collection",
)
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read configuration and initialise class variables."""
config = config_entry.options
self.config_entry = config_entry
self._attr_name = (
config_entry.title
if config_entry.title is not None
else config.get(CONF_NAME)
)
self._hidden = config.get(ATTR_HIDDEN, False)
self._manual = config.get(const.CONF_MANUAL)
first_month = config.get(const.CONF_FIRST_MONTH, const.DEFAULT_FIRST_MONTH)
months = [m["value"] for m in const.MONTH_OPTIONS]
self._first_month: int = (
months.index(first_month) + 1 if first_month in months else 1
)
last_month = config.get(const.CONF_LAST_MONTH, const.DEFAULT_LAST_MONTH)
self._last_month: int = (
months.index(last_month) + 1 if last_month in months else 12
)
self._verbose_state = config.get(const.CONF_VERBOSE_STATE)
self._icon_normal = config.get(const.CONF_ICON_NORMAL)
self._icon_today = config.get(const.CONF_ICON_TODAY)
self._icon_tomorrow = config.get(const.CONF_ICON_TOMORROW)
exp = config.get(const.CONF_EXPIRE_AFTER)
self.expire_after: time | None = (
None
if (
exp is None
or datetime.strptime(exp, "%H:%M:%S").time() == time(0, 0, 0)
)
else datetime.strptime(exp, "%H:%M:%S").time()
)
self._date_format = config.get(
const.CONF_DATE_FORMAT, const.DEFAULT_DATE_FORMAT
)
self._verbose_format = config.get(
const.CONF_VERBOSE_FORMAT, const.DEFAULT_VERBOSE_FORMAT
)
self._holiday_country = config.get(
const.CONF_HOLIDAY_COUNTRY, const.DEFAULT_HOLIDAY_COUNTRY
)
if config.get(const.CONF_MOVE_COUNTRY_HOLIDAYS, False):
try:
self._holidays_calendar = holidays.country_holidays(
self._holiday_country
)
except NotImplementedError:
_LOGGER.error(
"(%s) Unknown country code for holidays: %s",
self._attr_name,
self._holiday_country,
)
self._holidays_calendar = None
else:
self._holidays_calendar = None
self._collection_dates: list[date] = []
self._next_date: date | None = None
self._last_updated: datetime | None = None
self.last_collection: datetime | None = None
self._days: int | None = None
self._attr_state = "" if bool(self._verbose_state) else 2
self._attr_icon = self._icon_normal
async def async_added_to_hass(self) -> None:
"""When sensor is added to hassio, add it to calendar."""
await super().async_added_to_hass()
self.hass.data[const.DOMAIN][const.SENSOR_PLATFORM][self.entity_id] = self
# Restore stored state
if (state := await self.async_get_last_state()) is not None:
self._last_updated = None # Unblock update - after options change
self._attr_state = state.state
self._days = (
state.attributes[const.ATTR_DAYS]
if const.ATTR_DAYS in state.attributes
else None
)
next_date = (
helpers.parse_datetime(state.attributes[const.ATTR_NEXT_DATE])
if const.ATTR_NEXT_DATE in state.attributes
else None
)
self._next_date = None if next_date is None else next_date.date()
self.last_collection = (
helpers.parse_datetime(state.attributes[const.ATTR_LAST_COLLECTION])
if const.ATTR_LAST_COLLECTION in state.attributes
else None
)
# Create device
device_registry = dr.async_get(self.hass)
device_registry.async_get_or_create(
config_entry_id=self.config_entry.entry_id,
identifiers={(const.DOMAIN, self.unique_id)},
name=self._attr_name,
manufacturer="bruxy70",
)
# Create or add to calendar
if not self.hidden:
if const.CALENDAR_PLATFORM not in self.hass.data[const.DOMAIN]:
self.hass.data[const.DOMAIN][
const.CALENDAR_PLATFORM
] = EntitiesCalendarData(self.hass)
_LOGGER.debug("Creating garbage_collection calendar")
await self.hass.config_entries.async_forward_entry_setup(
self.config_entry, const.CALENDAR_PLATFORM
)
self.hass.data[const.DOMAIN][const.CALENDAR_PLATFORM].add_entity(
self.entity_id
)
async def async_will_remove_from_hass(self) -> None:
"""When sensor is added to hassio, remove it."""
await super().async_will_remove_from_hass()
del self.hass.data[const.DOMAIN][const.SENSOR_PLATFORM][self.entity_id]
self.hass.data[const.DOMAIN][const.CALENDAR_PLATFORM].remove_entity(
self.entity_id
)
@property
def unique_id(self) -> str:
"""Return a unique ID to use for this sensor."""
if "unique_id" in self.config_entry.data: # From legacy config
return self.config_entry.data["unique_id"]
return self.config_entry.entry_id
@property
def device_info(self) -> DeviceInfo | None:
"""Return device info."""
return {
"identifiers": {(const.DOMAIN, self.unique_id)},
"name": self.config_entry.data.get("name"),
"manufacturer": "bruxy70",
}
@property
def name(self) -> str | None:
"""Return the name of the sensor."""
return self._attr_name
@property
def next_date(self) -> date | None:
"""Return next date attribute."""
return self._next_date
@property
def hidden(self) -> bool:
"""Return the hidden attribute."""
return self._hidden
@property
def native_unit_of_measurement(self) -> str | None:
"""Return unit of measurement - None for numerical value."""
return None
@property
def native_value(self) -> object:
"""Return the state of the sensor."""
return self._attr_state
@property
def last_updated(self) -> datetime | None:
"""Return when the sensor was last updated."""
return self._last_updated
@property
def icon(self) -> str:
"""Return the entity icon."""
return self._attr_icon
@property
def extra_state_attributes(self) -> Dict[str, Any]:
"""Return the state attributes."""
state_attr = {
const.ATTR_DAYS: self._days,
const.ATTR_LAST_COLLECTION: self.last_collection,
const.ATTR_LAST_UPDATED: self._last_updated,
const.ATTR_NEXT_DATE: None
if self._next_date is None
else datetime(
self._next_date.year, self._next_date.month, self._next_date.day
).astimezone(),
# Needed for translations to work
ATTR_DEVICE_CLASS: self.DEVICE_CLASS,
}
return state_attr
@property
def DEVICE_CLASS(self) -> str: # pylint: disable=C0103
"""Return the class of the sensor."""
return const.DEVICE_CLASS
def __repr__(self) -> str:
"""Return main sensor parameters."""
return (
f"{self.__class__.__name__}(name={self._attr_name}, "
f"entity_id={self.entity_id}, "
f"state={self.state}, "
f"attributes={self.extra_state_attributes})"
)
def _find_candidate_date(self, day1: date) -> date | None:
"""Find the next possible date starting from day1.
Only based on calendar, not looking at include/exclude days.
Must be implemented for each child class.
"""
raise NotImplementedError
async def _async_ready_for_update(self) -> bool:
"""Check if the entity is ready for the update.
Skip the update if the sensor was updated today
Except for the sensors with with next date today and after the expiration time
"""
current_date_time = helpers.now()
today = current_date_time.date()
try:
ready_for_update = bool(self._last_updated.date() != today) # type: ignore
except AttributeError:
return True
try:
if self._next_date == today and (
(
isinstance(self.expire_after, time)
and current_date_time.time() >= self.expire_after
)
or (
isinstance(self.last_collection, datetime)
and self.last_collection.date() == today
)
):
return True
except (AttributeError, TypeError):
pass
return ready_for_update
def date_inside(self, dat: date) -> bool:
"""Check if the date is inside first and last date."""
month = dat.month
if self._first_month <= self._last_month:
return bool(self._first_month <= month <= self._last_month)
return bool(self._first_month <= month or month <= self._last_month)
def move_to_range(self, day: date) -> date:
"""If the date is not in range, move to the range."""
if not self.date_inside(day):
year = day.year
month = day.month
months = [m["label"] for m in const.MONTH_OPTIONS]
if self._first_month <= self._last_month < month:
_LOGGER.debug(
"(%s) %s outside the range, lookig from %s next year",
self._attr_name,
day,
months[self._first_month - 1],
)
return date(year + 1, self._first_month, 1)
_LOGGER.debug(
"(%s) %s outside the range, searching from %s",
self._attr_name,
day,
months[self._first_month - 1],
)
return date(year, self._first_month, 1)
return day
def collection_schedule(
self, date1: date | None = None, date2: date | None = None
) -> Generator[date, None, None]:
"""Get dates within configured date range."""
today = helpers.now().date()
first_date: date = date(today.year - 1, 1, 1) if date1 is None else date1
last_date: date = date(today.year + 1, 12, 31) if date2 is None else date2
first_date = self.move_to_range(first_date)
while True:
try:
next_date = self._find_candidate_date(first_date)
except (TypeError, ValueError):
return
if next_date is None or next_date > last_date:
return
if (new_date := self.move_to_range(next_date)) != next_date:
first_date = new_date # continue from next year
else:
yield next_date
first_date = next_date + relativedelta(days=1) # look from the next day
def _shift_for_holiday(self, collection_date: date) -> date:
"""Move the date forward if a public holiday falls on it or earlier that week.
First check Monday..collection_date (inclusive) for a holiday: if any day in
that range is a holiday, move forward by one day. Then keep moving forward,
one day at a time, while the new candidate day is itself a holiday.
"""
if self._holidays_calendar is None:
return collection_date
monday = collection_date - timedelta(days=collection_date.weekday())
week_has_holiday = any(
(monday + timedelta(days=i)) in self._holidays_calendar
for i in range(collection_date.weekday() + 1)
)
if not week_has_holiday:
return collection_date
shifted = collection_date + timedelta(days=1)
while shifted in self._holidays_calendar:
shifted = shifted + timedelta(days=1)
_LOGGER.debug(
"(%s) %s shifted to %s because of a public holiday",
self._attr_name,
collection_date,
shifted,
)
return shifted
async def _async_load_collection_dates(self) -> None:
"""Fill the collection dates list."""
self._collection_dates.clear()
for collection_date in self.collection_schedule():
collection_date = self._shift_for_holiday(collection_date)
if collection_date not in self._collection_dates:
self._collection_dates.append(collection_date)
self._collection_dates.sort()
async def add_date(self, collection_date: date) -> None:
"""Add date to _collection_dates."""
if collection_date not in self._collection_dates:
self._collection_dates.append(collection_date)
self._collection_dates.sort()
else:
_LOGGER.warning(
"%s not added to %s - already on the collection schedule",
collection_date,
self.name,
)
async def remove_date(self, collection_date: date) -> None:
"""Remove date from _collection dates."""
try:
self._collection_dates.remove(collection_date)
except ValueError:
_LOGGER.warning(
"%s not removed from %s - not in the collection schedule",
collection_date,
self.name,
)
def get_next_date(self, first_date: date, ignore_today=False) -> date | None:
"""Get next date from self._collection_dates."""
current_date_time = helpers.now()
for d in self._collection_dates: # pylint: disable=invalid-name
if d < first_date:
continue
if not ignore_today and d == current_date_time.date():
expiration = (
self.expire_after
if self.expire_after is not None
else time(23, 59, 59)
)
if current_date_time.time() > expiration or (
self.last_collection is not None
and self.last_collection.date() == current_date_time.date()
and current_date_time.time() >= self.last_collection.time()
):
continue
return d
return None
async def async_update(self) -> None:
"""Get the latest data and updates the states."""
if not await self._async_ready_for_update() or not self.hass.is_running:
return
_LOGGER.debug("(%s) Calling update", self._attr_name)
await self._async_load_collection_dates()
_LOGGER.debug(
"(%s) Dates loaded, firing a garbage_collection_loaded event",
self._attr_name,
)
event_data = {
"entity_id": self.entity_id,
"collection_dates": helpers.dates_to_texts(self._collection_dates),
}
self.hass.bus.async_fire("garbage_collection_loaded", event_data)
if not self._manual:
self.update_state()
def update_state(self) -> None:
"""Pick the first event from collection dates, update attributes."""
_LOGGER.debug("(%s) Looking for next collection", self._attr_name)
self._last_updated = helpers.now()
today = self._last_updated.date()
self._next_date = self.get_next_date(today)
if self._next_date is not None:
_LOGGER.debug(
"(%s) next_date (%s), today (%s)",
self._attr_name,
self._next_date,
today,
)
self._days = (self._next_date - today).days
next_date_txt = self._next_date.strftime(self._date_format)
_LOGGER.debug(
"(%s) Found next collection date: %s, that is in %d days",
self._attr_name,
next_date_txt,
self._days,
)
if self._days > 1:
if bool(self._verbose_state):
self._attr_state = self._verbose_format.format(
date=next_date_txt, days=self._days
)
# self._attr_state = "on_date"
else:
self._attr_state = 2
self._attr_icon = self._icon_normal
else:
if self._days == 0:
if bool(self._verbose_state):
self._attr_state = const.STATE_TODAY
else:
self._attr_state = self._days
self._attr_icon = self._icon_today
elif self._days == 1:
if bool(self._verbose_state):
self._attr_state = const.STATE_TOMORROW
else:
self._attr_state = self._days
self._attr_icon = self._icon_tomorrow
else:
self._days = None
self._attr_state = None
self._attr_icon = self._icon_normal
class WeeklyCollection(GarbageCollection):
"""Collection every n weeks, odd weeks or even weeks."""
__slots__ = "_collection_days", "_first_week", "_period"
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read parameters specific for Weekly Collection Frequency."""
super().__init__(config_entry)
config = config_entry.options
self._collection_days = config.get(const.CONF_COLLECTION_DAYS, [])
self._period: int
self._first_week: int
frequency = config.get(const.CONF_FREQUENCY)
if frequency == "weekly":
self._period = 1
self._first_week = 1
elif frequency == "even-weeks":
self._period = 2
self._first_week = 2
elif frequency == "odd-weeks":
self._period = 2
self._first_week = 1
else:
self._period = config.get(const.CONF_PERIOD, 1)
self._first_week = config.get(const.CONF_FIRST_WEEK, 1)
def _find_candidate_date(self, day1: date) -> date | None:
"""Calculate possible date, for weekly frequency."""
week = day1.isocalendar()[1]
weekday = day1.weekday()
offset = -1
if (week - self._first_week) % self._period == 0: # Collection this week
for day_name in self._collection_days:
day_index = WEEKDAYS.index(day_name)
if day_index >= weekday: # Collection still did not happen
offset = day_index - weekday
break
iterate_by_week = 7 - weekday + WEEKDAYS.index(self._collection_days[0])
while offset == -1: # look in following weeks
candidate = day1 + relativedelta(days=iterate_by_week)
week = candidate.isocalendar()[1]
if (week - self._first_week) % self._period == 0:
offset = iterate_by_week
break
iterate_by_week += 7
return day1 + relativedelta(days=offset)
class DailyCollection(GarbageCollection):
"""Collection every n days."""
__slots__ = "_first_date", "_period"
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read parameters specific for Daily Collection Frequency."""
super().__init__(config_entry)
config = config_entry.options
self._period = config.get(const.CONF_PERIOD)
self._first_date: date | None
try:
self._first_date = helpers.to_date(config.get(const.CONF_FIRST_DATE))
except ValueError:
self._first_date = None
def _find_candidate_date(self, day1: date) -> date | None:
"""Calculate possible date, for every-n-days frequency."""
try:
if (day1 - self._first_date).days % self._period == 0: # type: ignore
return day1
offset = self._period - (
(day1 - self._first_date).days % self._period # type: ignore
)
except TypeError as error:
raise ValueError(
f"({self._attr_name}) Please configure first_date and period "
"for every-n-days collection frequency."
) from error
return day1 + relativedelta(days=offset)
class MonthlyCollection(GarbageCollection):
"""Collection every nth weekday of each month."""
__slots__ = (
"_collection_days",
"_monthly_force_week_numbers",
"_period",
"_weekday_order_numbers",
"_week_order_numbers",
)
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read parameters specific for Monthly Collection Frequency."""
super().__init__(config_entry)
config = config_entry.options
self._collection_days = config.get(const.CONF_COLLECTION_DAYS, [])
self._monthly_force_week_numbers = config.get(
const.CONF_FORCE_WEEK_NUMBERS, False
)
self._weekday_order_numbers: list
self._week_order_numbers: list
order_numbers: list = []
if const.CONF_WEEKDAY_ORDER_NUMBER in config:
order_numbers = list(map(int, config[const.CONF_WEEKDAY_ORDER_NUMBER]))
if self._monthly_force_week_numbers:
self._weekday_order_numbers = []
self._week_order_numbers = order_numbers
else:
self._weekday_order_numbers = order_numbers
self._week_order_numbers = []
self._period = config.get(const.CONF_PERIOD, 1)
@staticmethod
def nth_week_date(
week_number: int, date_of_month: date, collection_day: int
) -> date:
"""Find weekday in the nth week of the month."""
first_of_month = date(date_of_month.year, date_of_month.month, 1)
return first_of_month + relativedelta(
days=collection_day - first_of_month.weekday() + (week_number - 1) * 7
)
@staticmethod
def nth_weekday_date(
weekday_number: int, date_of_month: date, collection_day: int
) -> date:
"""Find nth weekday of the month."""
first_of_month = date(date_of_month.year, date_of_month.month, 1)
# 1st of the month is before the day of collection
# (so 1st collection week the week when month starts)
if collection_day >= first_of_month.weekday():
return first_of_month + relativedelta(
days=collection_day
- first_of_month.weekday()
+ (weekday_number - 1) * 7
)
return first_of_month + relativedelta(
days=7
- first_of_month.weekday()
+ collection_day
+ (weekday_number - 1) * 7
)
def _monthly_candidate(self, day1: date) -> date:
"""Calculate possible date, for monthly frequency."""
if self._monthly_force_week_numbers:
for week_order_number in self._week_order_numbers:
candidate_date = MonthlyCollection.nth_week_date(
week_order_number, day1, WEEKDAYS.index(self._collection_days[0])
)
# date is today or in the future -> we have the date
if candidate_date >= day1:
return candidate_date
else:
for weekday_order_number in self._weekday_order_numbers:
candidate_date = MonthlyCollection.nth_weekday_date(
weekday_order_number,
day1,
WEEKDAYS.index(self._collection_days[0]),
)
# date is today or in the future -> we have the date
if candidate_date >= day1:
return candidate_date
if day1.month == 12:
next_collection_month = date(day1.year + 1, 1, 1)
else:
next_collection_month = date(day1.year, day1.month + 1, 1)
if self._monthly_force_week_numbers:
return MonthlyCollection.nth_week_date(
self._week_order_numbers[0],
next_collection_month,
WEEKDAYS.index(self._collection_days[0]),
)
return MonthlyCollection.nth_weekday_date(
self._weekday_order_numbers[0],
next_collection_month,
WEEKDAYS.index(self._collection_days[0]),
)
def _find_candidate_date(self, day1: date) -> date | None:
if self._period is None or self._period == 1:
return self._monthly_candidate(day1)
else:
candidate_date = self._monthly_candidate(day1)
while (candidate_date.month - self._first_month) % self._period != 0:
candidate_date = self._monthly_candidate(
candidate_date + relativedelta(days=1)
)
return candidate_date
class AnnualCollection(GarbageCollection):
"""Collection every year."""
__slots__ = ("_date",)
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read parameters specific for Annual Collection Frequency."""
super().__init__(config_entry)
config = config_entry.options
self._date = config.get(const.CONF_DATE)
def _find_candidate_date(self, day1: date) -> date | None:
"""Calculate possible date, for annual frequency."""
year = day1.year
try:
conf_date = datetime.strptime(self._date, "%m/%d").date()
except TypeError as error:
raise ValueError(
f"({self._attr_name}) Please configure the date "
"for annual collection frequency."
) from error
if (candidate_date := date(year, conf_date.month, conf_date.day)) < day1:
candidate_date = date(year + 1, conf_date.month, conf_date.day)
return candidate_date
class GroupCollection(GarbageCollection):
"""Group number of sensors."""
__slots__ = ("_entities",)
def __init__(self, config_entry: ConfigEntry) -> None:
"""Read parameters specific for Group Collection Frequency."""
super().__init__(config_entry)
config = config_entry.options
self._entities = config.get(CONF_ENTITIES, [])
def _find_candidate_date(self, day1: date) -> date | None:
"""Calculate possible date, for group frequency."""
candidate_date = None
try:
for entity_id in self._entities:
entity: GarbageCollection = self.hass.data[const.DOMAIN][
const.SENSOR_PLATFORM
][entity_id]
next_date = entity.get_next_date(day1)
if next_date is not None and (
candidate_date is None or next_date < candidate_date
):
candidate_date = next_date
except KeyError as error:
raise ValueError from error
except TypeError as error:
_LOGGER.error("(%s) Please add entities for the group.", self._attr_name)
raise ValueError from error
return candidate_date
async def _async_ready_for_update(self) -> bool:
"""Check if the entity is ready for the update.
For group sensors wait for update of the sensors in the group
"""
current_date_time = helpers.now()
today = current_date_time.date()
try:
ready_for_update = bool(self._last_updated.date() != today) # type: ignore
except AttributeError:
ready_for_update = True
members_ready = True
for entity_id in self._entities:
try:
entity: GarbageCollection = self.hass.data[const.DOMAIN][
const.SENSOR_PLATFORM
][entity_id]
await entity.async_update()
except KeyError:
members_ready = False
break
if (last_updated := entity.last_updated) is None:
ready_for_update = True
continue
# Wait for all members to get updated
if last_updated.date() != today:
members_ready = False
break
# A member got updated after the group update
if self._last_updated is None or last_updated > self._last_updated:
ready_for_update = True
if ready_for_update and not members_ready:
ready_for_update = False
return ready_for_update
class BlankCollection(GarbageCollection):
"""No collection - for mnual update."""
def _find_candidate_date(self, day1: date) -> date | None:
"""Do not return any date for blank frequency."""
return None
async def _async_load_collection_dates(self) -> None:
"""Clear collection dates (filled in by the blueprint)."""
self._collection_dates.clear()
return
async def async_update(self) -> None:
"""Get the latest data and updates the states."""
if not await self._async_ready_for_update() or not self.hass.is_running:
return
_LOGGER.debug("(%s) Calling update", self._attr_name)
await self._async_load_collection_dates()
_LOGGER.debug(
"(%s) Dates loaded, firing a garbage_collection_loaded event",
self._attr_name,
)
event_data = {
"entity_id": self.entity_id,
"collection_dates": [],
}
self.hass.bus.async_fire("garbage_collection_loaded", event_data)
@@ -0,0 +1,60 @@
collect_garbage:
description: Set the last_collection attribute to the current date and time.
target:
entity:
integration: garbage_collection
fields:
entity_id:
description: The garbage_collection sensor entity_id
example: sensor.general_waste
last_collection:
description: Date and time of the last collection (optional)
example: "2020-08-16 10:54:00"
add_date:
description: Manually add collection date.
target:
entity:
integration: garbage_collection
fields:
entity_id:
description: The garbage_collection sensor entity_id
example: sensor.general_waste
date:
description: Collection date to add
example: '"2020-08-16"'
offset_date:
description: Move the collection date by a number of days.
target:
entity:
integration: garbage_collection
fields:
entity_id:
description: The garbage_collection sensor entity_id
example: sensor.general_waste
date:
description: Collection date to move
example: '"2020-08-16"'
offset:
description: Nuber of days to move (negative number will move it back)
example: 1
remove_date:
description: Remove automatically calculated collection date.
target:
entity:
integration: garbage_collection
fields:
entity_id:
description: The garbage_collection sensor entity_id
example: sensor.general_waste
date:
description: Collection date to remove
example: '"2020-08-16"'
update_state:
description: Update the entity state and attributes. Used with the manual_update option, do defer the update after changing the automatically created schedule by automation trigered by the garbage_collection_loaded event.
target:
entity:
integration: garbage_collection
fields:
entity_id:
description: The garbage_collection sensor entity_id
example: sensor.general_waste
@@ -0,0 +1,104 @@
{
"config": {
"step": {
"user": {
"title": "Garbage Collection - Konfigurace (1/2)",
"description": "Zadej jméno sensoru a nastav parametry. Více na https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Friendly name",
"hidden": "Skrýt v kalendáři",
"frequency": "Frekvence",
"manual_update": "Manual update - state je aktualizovaný manuálně voláním služby",
"icon_normal": "Ikona (mdi:trash-can)",
"icon_tomorrow": "Ikona svoz zítra (mdi:delete-restore)",
"icon_today": "Ikona svoz dnes (mdi:delete-circle)",
"expire_after": "Čas expirace (HH:MM)",
"verbose_state": "Verbose state (popis místo čísel)"
}
},
"detail": {
"title": "Garbage Collection - Další parametry (2/2)",
"description": "Více na: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Datum (mm/dd)",
"entities": "Seznam entit (odděleno čárkou)",
"collection_days": "Dny svozu",
"first_month": "První měsíc svozu",
"last_month": "Poslední měsíc svozu",
"period": "Perioda (svoz každých n týdnů/dnů): (1-1000)",
"first_week": "První týden svozu (1-52)",
"first_date": "První datum",
"weekday_order_number": "Pořadí dne v měsící (např. první středa v měsíci)",
"force_week_order_numbers": "Číslo týdne v měsící místo čísla dne",
"verbose_format": "Verbose format (Použij `date` a `days` proměnné (ve složených závorkách))",
"date_format": "Formát data(see http://strftime.org/)"
}
}
},
"error": {
"value": "Chybná hodnota. Zkontroluj zadané hodnoty!",
"icon": "Ikony musí být zadány ve formátu 'prefix:jmnéno'.",
"days": "Vyber jeden nebo více dní!",
"entities": "Entita neexistuje!",
"month_day": "Špatný formát data!",
"time": "Špatný formát času!",
"weekday_order_number": "Vyber jeden nebo více dní!",
"week_order_number": "Vyber jeden nebo více týdnů!",
"period": "Perioda musí být číslo mezi 1 a 1000",
"first_week": "První týden musí být číslo mezi 1 a 52",
"date": "Špatný formát data!"
},
"abort": {
"single_instance_allowed": "Only a single configuration of Garbage Collection is allowed."
}
},
"options": {
"step": {
"init": {
"title": "Garbage Collection - Konfigurace (1/2)",
"description": "Zadej jméno sensoru a nastav parametry. Více na https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Skrýt v kalendáři",
"frequency": "Frekvence",
"manual_update": "State je aktualizovaný manuálně voláním služby",
"icon_normal": "Ikona (mdi:trash-can)",
"icon_tomorrow": "Ikona svoz zítra (mdi:delete-restore)",
"icon_today": "Ikona svoz dnes (mdi:delete-circle)",
"expire_after": "Čas expirace (HH:MM)",
"verbose_state": "Verbose state (popis místo čísel)"
}
},
"detail": {
"title": "Garbage Collection - Další parametry (2/2)",
"description": "",
"data": {
"date": "Datum (mm/dd)",
"entities": "Seznam entit (odděleno čárkou)",
"collection_days": "Dny svozu",
"first_month": "První měsíc svozu",
"last_month": "Poslední měsíc scozu",
"period": "Perioda (svoz každých n týdnů-dnů): (1-1000)",
"first_week": "První týden svozu (1-52)",
"first_date": "První datum",
"weekday_order_number": "Pořadí dne v měsící (např. první středa v měsíci)",
"force_week_order_numbers": "Číslo týdne v měsící místo čísla dne",
"verbose_format": "Verbose format (Použij `date` a `days` proměnné (ve složených závorkách))",
"date_format": "Formát data(see http://strftime.org/)"
}
}
},
"error": {
"value": "Chybná hodnota. Zkontroluj zadané hodnoty!",
"icon": "Ikony musí být zadány ve formátu 'prefix:jmnéno'.",
"days": "Vyber jeden nebo více dní!",
"entities": "Entita neexistuje!",
"month_day": "Špatný formát data!",
"time": "Špatný formát času!",
"weekday_order_number": "Vyber jeden nebo více dní!",
"week_order_number": "Vyber jeden nebo více týdnů!",
"period": "Perioda musí být číslo mezi 1 a 1000",
"first_week": "První týden musí být číslo mezi 1 a 52",
"date": "Špatný formát data!"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Garbage Collection - Afhentningsfrekvens (1/2)",
"description": "Angiv navnet på sensoren og konfigurer sensorparametre. For mere info se: https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Visningsnavn",
"hidden": "Skjul i kalender",
"frequency": "Frekvens",
"manual_update": "Manuel opdatering - sensortilstand opdateres manuelt af en service (blueprint)",
"icon_normal": "Ikon (mdi:trash-can) - ikke påkrævet",
"icon_tomorrow": "Ikon ved afhentning i morgen (mdi:delete-restore) - ikke påkrævet",
"icon_today": "Ikon ved afhentning i dag (mdi:delete-circle) - ikke påkrævet",
"expire_after": "Forældes efter (HH:MM) - ikke påkrævet",
"verbose_state": "Verbos tilstand (tekst i stedet for tal)"
}
},
"detail": {
"title": "Garbage Collection - Yderligere parametre (2/2)",
"description": "For mere info se: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Dato (mm/dd)",
"entities": "Liste af enheder (kommasepareret)",
"collection_days": "Afhentningsdage",
"first_month": "Første afhentningsmåned",
"last_month": "Sidste afhentningsmåned",
"period": "Afhentning sker hver n uger/dage: (1-1000)",
"first_week": "Første afhentningsuge (1-52)",
"first_date": "Første dato",
"weekday_order_number": "Specifikke ugedage i måneden (fx første onsdag i måneden)",
"force_week_order_numbers": "Brug specifik uge i måneden i stedet for specifik ugedag (fx onsdag i første uge i måneden)",
"verbose_format": "Verbost format (anvend `date` og `days` variablerne (i tuborgklammer))",
"date_format": "Datoformat (se http://strftime.org/)"
}
}
},
"error": {
"value": "Ugyldig værdi. Check dit input!",
"icon": "Ikoner skal angives i formen 'præfiks:navn'.",
"days": "Vælg en eller flere dage!",
"entities": "Enheden findes ikke!",
"month_day": "Ugyldigt dato format!",
"time": "Ugyldigt tids format!",
"weekday_order_number": "Vælg en eller flere dage",
"week_order_number": "Vælg en eller flere uger",
"period": "Afhentningsperioden skal være et tal mellem 1 og 1000",
"first_week": "Første afhentningsuge skal være et tal mellem 1 and 52",
"date": "Ugyldigt dato format!"
},
"abort": {
"single_instance_allowed": "Det er kun tilladt at have én konfiguration af Garbage Collection."
}
},
"options": {
"step": {
"init": {
"title": "Garbage Collection - Afhentningsfrekvens (1/2)",
"description": "Ændr sensorparametre. More info on https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Skjul i kalender",
"frequency": "Frekvens",
"manual_update": "Manuel opdatering - sensortilstand opdateres manuelt af en service (blueprint)",
"icon_normal": "Ikon (mdi:trash-can) - ikke påkrævet",
"icon_tomorrow": "Ikon ved afhentning i morgen (mdi:delete-restore) - ikke påkrævet",
"icon_today": "Ikon ved afhentning i dag (mdi:delete-circle) - ikke påkrævet",
"expire_after": "Forældes efter (HH:MM) - ikke påkrævet",
"verbose_state": "Verbos tilstand (tekst i stedet for tal)"
}
},
"detail": {
"title": "Garbage Collection - Yderligere parametre (2/2)",
"description": "For mere info se: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Dato (mm/dd)",
"entities": "Liste af enheder (kommasepareret)",
"collection_days": "Afhentningsdage",
"first_month": "Første afhentningsmåned",
"last_month": "Sidste afhentningsmåned",
"period": "Afhentning sker hver n uger/dage: (1-1000)",
"first_week": "Første afhentningsuge (1-52)",
"first_date": "Første dato",
"weekday_order_number": "Specifikke ugedage i måneden (fx første onsdag i måneden)",
"force_week_order_numbers": "Brug specifik uge i måneden i stedet for specifik ugedag (fx onsdag i første uge i måneden)",
"verbose_format": "Verbost format (anvend `date` og `days` variablerne (i tuborgklammer))",
"date_format": "Datoformat (se http://strftime.org/)"
}
}
},
"error": {
"value": "Ugyldig værdi. Check dit input!",
"icon": "Ikoner skal angives i formen 'præfiks:navn'.",
"days": "Vælg en eller flere dage!",
"entities": "Enheden findes ikke!",
"month_day": "Ugyldigt dato format!",
"time": "Ugyldigt tids format!",
"weekday_order_number": "Vælg en eller flere dage",
"week_order_number": "Vælg en eller flere uger",
"period": "Afhentningsperioden skal være et tal mellem 1 og 1000",
"first_week": "Første afhentningsuge skal være et tal mellem 1 and 52",
"date": "Ugyldigt dato format!"
}
}
}
@@ -0,0 +1,104 @@
{
"config": {
"step": {
"user": {
"title": "Müllabfuhr - Häufigkeit der Abholung (1/2)",
"description": "Geben Sie den Sensornamen ein und konfigurieren Sie die Sensorparameter. Mehr Informationen unter https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Friendly Name",
"hidden": "Im Kalender ausblenden",
"frequency": "Häufigkeit",
"manual_update": "Manuelle Aktualisierung - Sensorstatus wird manuell durch einen Dienst aktualisiert (Vorlage)",
"icon_normal": "Icon (mdi:trash-can) - optional",
"icon_tomorrow": "Icon Abfuhr morgen (mdi:delete-restore) - optional",
"icon_today": "Icon Abfuhr heute (mdi:delete-circle) - optional",
"expire_after": "Ablauf nach (HH:MM) - optional",
"verbose_state": "Ausführlicher Status (Text, statt Zahl)"
}
},
"detail": {
"title": "Müllabfuhr - Zusätzliche Parameter (2/2)",
"description": "Mehr Details hier: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Datum (mm/dd)",
"entities": "Liste der Entitäten (durch Komma getrennt)",
"collection_days": "Tage der Abholung",
"first_month": "Erster Abholungsmonat",
"last_month": "Letzter Abholungsmonat",
"period": "Abholung alle n Wochen/Tage: (1-1000)",
"first_week": "Erste Abholungswoche (1-52)",
"first_date": "Erstes Datum",
"weekday_order_number": "Reihenfolge der Wochentage im Monat (z. B. erster Mittwoch im Monat)",
"force_week_order_numbers": "Reihenfolge der Woche im Monat statt Wochentagsreihenfolge (z. B. am Mittwoch der ersten Woche)",
"verbose_format": "Ausführliches Format (unter Verwendung der Variablen 'Datum' und 'Tage' (in eckigen Klammern))",
"date_format": "Datumsformat (siehe http://strftime.org/)"
}
}
},
"error": {
"value": "Ungültiger Wert. Bitte überprüfen Sie Ihre Eingabe!",
"icon": "Icons sollten in der Form 'Präfix:Name' angegeben werden.",
"days": "Wählen Sie 1 oder mehrere Tage aus!",
"entities": "Entität existiert nicht!",
"month_day": "Ungültiges Datumsformat!",
"time": "Ungültiges Zeitformat!",
"weekday_order_number": "Wählen Sie 1 oder mehrere Tage aus",
"week_order_number": "Wählen Sie 1 oder mehrere Wochen aus",
"period": "Zeitraum muss eine Zahl zwischen 1 und 1000 sein",
"first_week": "Erste Woche muss eine Zahl zwischen 1 und 52 sein",
"date": "Ungültiges Datumsformat!"
},
"abort": {
"single_instance_allowed": "Es ist nur eine einzige Konfiguration von Garbage Collection erlaubt."
}
},
"options": {
"step": {
"init": {
"title": "Müllabfuhr - Häufigkeit der Abholung (1/2)",
"description": "Sensorparameter ändern. Mehr Informationen unter https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Im Kalender ausblenden",
"frequency": "Häufigkeit",
"manual_update": "Manuelle Aktualisierung - Sensorstatus wird manuell durch einen Dienst aktualisiert (Vorlage)",
"icon_normal": "Icon (mdi:trash-can) - optional",
"icon_tomorrow": "Icon Abfuhr morgen (mdi:delete-restore) - optional",
"icon_today": "Icon Abfuhr heute (mdi:delete-circle) - optional",
"expire_after": "Ablauf nach (HH:MM) - optional",
"verbose_state": "Ausführlicher Status (Text, statt Zahl)"
}
},
"detail": {
"title": "Müllabfuhr - Zusätzliche Parameter (2/2)",
"description": "Mehr Details hier: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Datum (mm/dd)",
"entities": "Liste der Entitäten (durch Komma getrennt)",
"collection_days": "Tage der Abholung",
"first_month": "Erster Abholungsmonat",
"last_month": "Letzter Abholungsmonat",
"period": "Abholung alle n Wochen/Tage: (1-1000)",
"first_week": "Erste Abholungswoche (1-52)",
"first_date": "Erstes Datum",
"weekday_order_number": "Reihenfolge der Wochentage im Monat (z. B. erster Mittwoch im Monat)",
"force_week_order_numbers": "Reihenfolge der Woche im Monat statt Wochentagsreihenfolge (z. B. am Mittwoch der ersten Woche)",
"verbose_format": "Ausführliches Format (unter Verwendung der Variablen 'Datum' und 'Tage' (in eckigen Klammern))",
"date_format": "Datumsformat (siehe http://strftime.org/)"
}
}
},
"error": {
"value": "Ungültiger Wert. Bitte überprüfen Sie Ihre Eingabe!",
"icon": "Icons sollten in der Form 'Präfix:Name' angegeben werden.",
"days": "Wählen Sie 1 oder mehrere Tage aus!",
"entities": "Entität existiert nicht!",
"month_day": "Ungültiges Datumsformat!",
"time": "Ungültiges Zeitformat!",
"weekday_order_number": "Wählen Sie 1 oder mehrere Tage aus",
"week_order_number": "Wählen Sie 1 oder mehrere Wochen aus",
"period": "Zeitraum muss eine Zahl zwischen 1 und 1000 sein",
"first_week": "Erste Woche muss eine Zahl zwischen 1 und 52 sein",
"date": "Ungültiges Datumsformat!"
}
}
}
@@ -0,0 +1,108 @@
{
"config": {
"step": {
"user": {
"title": "Garbage Collection - Collection frequency (1/2)",
"description": "Enter the sensor name and configure sensor parameters. More info on https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Friendly name",
"hidden": "Hide in calendar",
"frequency": "Frequency",
"manual_update": "Manual update - sensor state updated manually by a service (Blueprint)",
"icon_normal": "Icon (mdi:trash-can) - optional",
"icon_tomorrow": "Icon collection tomorrow (mdi:delete-restore) - optional",
"icon_today": "Icon collection today (mdi:delete-circle) - optional",
"expire_after": "Expire after (HH:MM) - optional",
"verbose_state": "Verbose state (text, instead of number)",
"move_country_holidays": "Shift collection if a public holiday falls in the week",
"holiday_country": "Country code for public holidays (e.g. FR)"
}
},
"detail": {
"title": "Garbage Collection - Additional parameters (2/2)",
"description": "More details here: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Date (mm/dd)",
"entities": "List of entities (comma separated)",
"collection_days": "Collection days",
"first_month": "First collection month",
"last_month": "Last collection month",
"period": "Collection every n weeks/days: (1-1000)",
"first_week": "First collection week (1-52)",
"first_date": "First date",
"weekday_order_number": "Order of the weekday in the month (e.g. first Wednesday of the month)",
"force_week_order_numbers": "Order of week in a month instead of order of weekday (e.g. on Wednesday of the first week)",
"verbose_format": "Verbose format (using `date` and `days` variables (in squary brackets))",
"date_format": "Date format (see http://strftime.org/)"
}
}
},
"error": {
"value": "Invalid value. Please check your input!",
"icon": "Icons should be specified in the form 'prefix:name'.",
"days": "Select 1 or more days!",
"entities": "Entity does not exist!",
"month_day": "Invalid date format!",
"time": "Invalid time format!",
"weekday_order_number": "Select 1 or more days",
"week_order_number": "Select 1 or more weeks",
"period": "Period must be a number between 1 and 1000",
"first_week": "First week must be a number between 1 and 52",
"date": "Invalid date format!"
},
"abort": {
"single_instance_allowed": "Only a single configuration of Garbage Collection is allowed."
}
},
"options": {
"step": {
"init": {
"title": "Garbage Collection - Collection frequency (1/2)",
"description": "Change sensor parameters. More info on https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Hide in calendar",
"frequency": "Frequency",
"manual_update": "Manual update - sensor state updated manually by a service (Blueprint)",
"icon_normal": "Icon (mdi:trash-can) - optional",
"icon_tomorrow": "Icon collection tomorrow (mdi:delete-restore) - optional",
"icon_today": "Icon collection today (mdi:delete-circle) - optional",
"expire_after": "Expire after (HH:MM) - optional",
"verbose_state": "Verbose state (text, instead of number)",
"move_country_holidays": "Shift collection if a public holiday falls in the week",
"holiday_country": "Country code for public holidays (e.g. FR)"
}
},
"detail": {
"title": "Garbage Collection - Additional parameters (2/2)",
"description": "More details here: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Date (mm/dd)",
"entities": "List of entities (comma separated)",
"collection_days": "Collection days",
"first_month": "First collection month",
"last_month": "Last collection month",
"period": "Collection every n weeks/days: (1-1000)",
"first_week": "First collection week (1-52)",
"first_date": "First date",
"weekday_order_number": "Order of the weekday in the month (e.g. first Wednesday of the month)",
"force_week_order_numbers": "Order of week in a month instead of order of weekday (e.g. on Wednesday of the first week)",
"verbose_format": "Verbose format (using `date` and `days` variables (in squary brackets))",
"date_format": "Date format (see http://strftime.org/)"
}
}
},
"error": {
"value": "Invalid value. Please check your input!",
"icon": "Icons should be specified in the form 'prefix:name'.",
"days": "Select 1 or more days!",
"entities": "Entity does not exist!",
"month_day": "Invalid date format!",
"time": "Invalid time format!",
"weekday_order_number": "Select 1 or more days",
"week_order_number": "Select 1 or more weeks",
"period": "Period must be a number between 1 and 1000",
"first_week": "First week must be a number between 1 and 52",
"date": "Invalid date format!"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Recolección de basura - Frecuencia de recogida (1/2)",
"description": "Ingrese el nombre del sensor y configure los parámetros del sensor. Más información en https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Nombre amigable",
"hidden": "Esconderse en el calendario",
"frequency": "Frequencia",
"manual_update": "El estado del sensor se actualiza manualmente llamando a un servicio",
"icon_normal": "Icono (mdi:trash-can)",
"icon_tomorrow": "Icono de recoleccion para mañana (mdi:delete-restore)",
"icon_today": "Icono de recoleccion para hoy (mdi:delete-circle)",
"expire_after": "Caduca después (HH:MM)",
"verbose_state": "Estado detallado (texto, en lugar de número)"
}
},
"detail": {
"title": "Recolección de basura - Parámetros adicionales (2/2)",
"description": "Más detalles aquí: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Fecha (mm/dd)",
"entities": "Lista de entidades (separadas por comas)",
"collection_days": "Días de recogida",
"first_month": "Primer mes de recogida",
"last_month": "Último mes de recogida",
"period": "Recolección cada n semanas / días: (1-1000)",
"first_week": "Primera semana de recolección (1-52)",
"first_date": "Primera fecha",
"weekday_order_number": "Orden del día de la semana en el mes (por ejemplo, primer miércoles del mes)",
"force_week_order_numbers": "Orden de la semana en un mes en lugar de orden del día de la semana (por ejemplo, el miércoles de la primera semana)",
"verbose_format": "Formato detallado (usando las variables `fecha` y` dias` (entre corchetes))",
"date_format": "Formato de fecha(ver en http://strftime.org/)"
}
}
},
"error": {
"value": "Valor no válido. Por favor revise su entrada!",
"icon": "Los iconos deben especificarse en el formato 'prefix:name'.",
"days": "Seleccione 1 o más días!",
"entities": "La entidad no existe!",
"month_day": "Formato de fecha inválido!",
"time": "Formato de hora inválido!",
"weekday_order_number": "Seleccione 1 o más días",
"week_order_number": "Seleccione 1 o más semanas",
"period": "El período debe ser un número entre 1 y 1000",
"first_week": "La primera semana debe ser un número entre 1 y 52",
"date": "Formato de fecha inválido!"
},
"abort": {
"single_instance_allowed": "Solo se permite una única configuración de Recolección de basura."
}
},
"options": {
"step": {
"init": {
"title": "Recolección de basura - Frecuencia de recogida (1/2)",
"description": "Cambiar los parámetros del sensor. Más información en https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Esconderse en el calendario",
"frequency": "Frequencia",
"manual_update": "El estado del sensor se actualiza manualmente llamando a un servicio",
"icon_normal": "Icono (mdi:trash-can)",
"icon_tomorrow": "Icono de recoleccion para mañana (mdi:delete-restore)",
"icon_today": "Icono de recoleccion para hoy (mdi:delete-circle)",
"expire_after": "Caduca después (HH:MM)",
"verbose_state": "Estado detallado (texto, en lugar de número)"
}
},
"detail": {
"title": "Recolección de basura - Parámetros adicionales (2/2)",
"description": "Más detalles aquí: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Fecha (mm/dd)",
"entities": "Lista de entidades (separadas por comas)",
"collection_days": "Días de recogida",
"first_month": "Primer mes de recogida",
"last_month": "Último mes de recogida",
"period": "Recolección cada n semanas / días: (1-1000)",
"first_week": "Primera semana de recolección (1-52)",
"first_date": "Primera fecha",
"weekday_order_number": "Orden del día de la semana en el mes (por ejemplo, primer miércoles del mes)",
"force_week_order_numbers": "Orden de la semana en un mes en lugar de orden del día de la semana (por ejemplo, el miércoles de la primera semana)",
"verbose_format": "Formato detallado (usando las variables `fecha` y` dias` (entre corchetes))",
"date_format": "Formato de fecha(ver en http://strftime.org/)"
}
}
},
"error": {
"value": "Valor no válido. Por favor revise su entrada!",
"icon": "Los iconos deben especificarse en el formato 'prefix:name'.",
"days": "Seleccione 1 o más días!",
"entities": "La entidad no existe!",
"month_day": "Formato de fecha inválido!",
"time": "Formato de hora inválido!",
"weekday_order_number": "Seleccione 1 o más días",
"week_order_number": "Seleccione 1 o más semanas",
"period": "El período debe ser un número entre 1 y 1000",
"first_week": "La primera semana debe ser un número entre 1 y 52",
"date": "Formato de fecha inválido!"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Prügivedu - tühjendamiste ajad (1/2)",
"description": "Sisesta nduri andmed. Rohkem infot leiab https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Kuvatav nimi",
"hidden": "Ära näita kalendris",
"frequency": "Sagedus",
"manual_update": "Sensor state updated manually by calling a service",
"icon_normal": "Ikoon (mdi:trash-can) - valikuline",
"icon_tomorrow": "Homme on prügivedu ikoon (mdi:delete-restore) - valikuline",
"icon_today": "Täna on prügivedu ikoon (mdi:delete-circle) - valikuline",
"expire_after": "Ajalõpp (HH:MM) - valikuline",
"verbose_state": "Teavitus tekstina (aeg tekstina numbrite asemel)"
}
},
"detail": {
"title": "Prügivedu - täiendavad sätted (2/2)",
"description": "Vali üks või mitu tühjendamise nädalapäeva. Rohkem teavet: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Kuupäev (mm/dd)",
"entities": "Olemite nimekiri (komadega eraldatud)",
"collection_days": "Tühjendamise nädalapäevad",
"first_month": "Esimene prügiveo kuu",
"last_month": "Viimane prügiveo kuu",
"period": "Prügivedu iga n päeva/nädala tagant: (1-1000)",
"first_week": "Esimese prügiveo nädal (1-52)",
"first_date": "Esimese prügiveo kuupäev",
"weekday_order_number": "Kuu nädalapäevade järjekord (nt kuu esimene kolmapäev)",
"force_week_order_numbers": "nädala järjekord kuus, mitte nädalapäevade järjekord (nt esimese nädala kolmapäeval).",
"verbose_format": "Teavituse formaat (kasutades `date` and `days` muutujaid (kantsulgudes))",
"date_format": "Kuupäeva formaat (vaata http://strftime.org/)"
}
}
},
"error": {
"value": "Vigane sisestus, palun kontrolli!",
"icon": "Ikoonid tuleb esitada kujul 'prefix:name'.",
"days": "Valige üks või rohkem päevi!",
"entities": "Olem puudub!",
"month_day": "Vigane kuupäeva formaat!",
"time": "Vigane kellaaja formaat!",
"weekday_order_number": "Valige üks või rohkem päevi",
"week_order_number": "Valige üks või rohkem nädalat",
"period": "Välp peab olema number 1 ja 1000 vahel",
"first_week": "Esimene nädal peab olema number 1 ja 52 vahel",
"date": "Vigane kuupäeva formaat!!"
},
"abort": {
"single_instance_allowed": "Lubatud on ainult üks prügiveo olemi sidumine."
}
},
"options": {
"step": {
"init": {
"title": "Prügivedu - tühjendamiste ajad (1/2)",
"description": "Sisesta nduri andmed. Rohkem infot leiab https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Ära näita kalendris",
"frequency": "Sagedus",
"manual_update": "Sensor state updated manually by calling a service",
"icon_normal": "Ikoon (mdi:trash-can) - valikuline",
"icon_tomorrow": "Homme on prügivedu ikoon (mdi:delete-restore) - valikuline",
"icon_today": "Täna on prügivedu ikoon (mdi:delete-circle) - valikuline",
"expire_after": "Ajalõpp (HH:MM) - valikuline",
"verbose_state": "Teavitus tekstina (aeg tekstina numbrite asemel)"
}
},
"detail": {
"title": "Prügivedu - täiendavad sätted (2/2)",
"description": "Vali üks või mitu tühjendamise nädalapäeva. Rohkem teavet: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Kuupäev (mm/dd)",
"entities": "Olemite nimekiri (komadega eraldatud)",
"collection_days": "Tühjendamise nädalapäevad",
"first_month": "Esimene prügiveo kuu",
"last_month": "Viimane prügiveo kuu",
"period": "Prügivedu iga n päeva/nädala tagant: (1-1000)",
"first_week": "Esimese prügiveo nädal (1-52)",
"first_date": "Esimese prügiveo kuupäev",
"weekday_order_number": "Kuu nädalapäevade järjekord (nt kuu esimene kolmapäev)",
"force_week_order_numbers": "nädala järjekord kuus, mitte nädalapäevade järjekord (nt esimese nädala kolmapäeval).",
"verbose_format": "Teavituse formaat (kasutades `date` and `days` muutujaid (kantsulgudes))",
"date_format": "Kuupäeva formaat (vaata http://strftime.org/)"
}
}
},
"error": {
"value": "Vigane sisestus, palun kontrolli!",
"icon": "Ikoonid tuleb esitada kujul 'prefix:name'.",
"days": "Valige üks või rohkem päevi!",
"entities": "Olem puudub!",
"month_day": "Vigane kuupäeva formaat!",
"time": "Vigane kellaaja formaat!",
"weekday_order_number": "Valige üks või rohkem päevi",
"week_order_number": "Valige üks või rohkem nädalat",
"period": "Välp peab olema number 1 ja 1000 vahel",
"first_week": "Esimene nädal peab olema number 1 ja 52 vahel",
"date": "Vigane kuupäeva formaat!!"
}
}
}
@@ -0,0 +1,110 @@
{
"config": {
"step": {
"user": {
"title": "Garbage Collection - Fréquence de la collecte (1/2)",
"description": "Définir le nom du capteur et configurer les paramètres du capteur. Plus d'info sur https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Friendly name",
"hidden": "Masquer dans le calendrier",
"frequency": "Fréquence",
"manual_update": "État du capteur mis à jour manuellement en appelant un service",
"icon_normal": "Icône (mdi:trash-can)",
"icon_tomorrow": "Icône pour collecte à jour J+1 (mdi:delete-restore)",
"icon_today": "Icône pour collecte au jour J (mdi:delete-circle)",
"offset": "Décalage (entre -31 et 31 jours)",
"expire_after": "Expire après (HH:MM)",
"verbose_state": "Etat verbeux (texte, au lieu du chiffre)",
"move_country_holidays": "Décaler la collecte si jour férié dans la semaine",
"holiday_country": "Code pays pour les jours fériés (ex: FR)"
}
},
"detail": {
"title": "Garbage Collection - Paramètres additionnels (2/2)",
"description": "Choisir un ou plusieurs jours de collecte. Plus de détail ici: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Date (mm/dd)",
"entities": "Liste des entités (séparées par une virgule)",
"collection_days": "Jours de collecte",
"first_month": "Premier mois de la collecte",
"last_month": "Dernier mois de la collecte",
"period": "Collecte toutes les n semaines/jours: (1-1000)",
"first_week": "Première semaine de la collecte (1-52)",
"first_date": "Première date",
"weekday_order_number": "Ordre du jour de la semaine dans le mois (par exemple, premier mercredi du mois)",
"force_week_order_numbers": "Nième occurrence du jour de la semaine dans un mois, au lieu du jour de la semaine dans la Nième semaine de chaque mois. Plus d'info sur https://github.com/bruxy70/Garbage-Collection#parameters-for-monthly-collection",
"verbose_format": "Format état verbeux (utilisation des variables `date` et `days` (entre caractères accolade ))",
"date_format": "Format de date (voir http://strftime.org/)"
}
}
},
"error": {
"value": "Valeur invalide. Veuillez vérifier votre saisie !",
"icon": "Les icônes doivent être spécifiées dans le format 'prefix:name'.",
"days": "Choisir un ou plusieurs jours !",
"entities": "L'entité n'existe pas !",
"month_day": "Format de date invalide !",
"time": "Format d'heure invalide !",
"weekday_order_number": "Choisir un ou plusieurs jours",
"week_order_number": "Choisir une ou plusieurs semaines",
"period": "La semaine doit être un nombre entre 1 et 1000",
"first_week": "La première semaine doit être un nombre entre 1 et 52",
"date": "Format de date invalide !"
},
"abort": {
"single_instance_allowed": "Une seule configuration de Garbage Collection est autorisée"
}
},
"options": {
"step": {
"init": {
"title": "Garbage Collection - Fréquence de la collecte (1/2)",
"description": "Modifier les paramètres des capteurs. Plus d'infos sur https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Masquer dans le calendrier",
"frequency": "Fréquence",
"manual_update": "État du capteur mis à jour manuellement en appelant un service",
"icon_normal": "Icône (mdi:trash-can)",
"icon_tomorrow": "Icône pour collecte à jour J+1 (mdi:delete-restore)",
"icon_today": "Icône pour collecte au jour J (mdi:delete-circle)",
"offset": "Décalage (entre -31 et 31 jours)",
"expire_after": "Expire après (HH:MM)",
"verbose_state": "Etat verbeux (texte, au lieu du chiffre)",
"move_country_holidays": "Décaler la collecte si jour férié dans la semaine",
"holiday_country": "Code pays pour les jours fériés (ex: FR)"
}
},
"detail": {
"title": "Garbage Collection - Paramètres additionnels (2/2)",
"description": "Choisir un ou plusieurs jours de collecte. Plus de détail ici: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Date (mm/dd)",
"entities": "Liste des entités (séparées par une virgule)",
"collection_days": "Jours de collecte",
"first_month": "Premier mois de la collecte",
"last_month": "Dernier mois de la collecte",
"period": "Collecte toutes les n semaines/jours: (1-1000)",
"first_week": "Première semaine de la collecte (1-52)",
"first_date": "Première date",
"weekday_order_number": "Ordre du jour de la semaine dans le mois (par exemple, premier mercredi du mois)",
"force_week_order_numbers": "Nième occurrence du jour de la semaine dans un mois, au lieu du jour de la semaine dans la Nième semaine de chaque mois. Plus d'info sur https://github.com/bruxy70/Garbage-Collection#parameters-for-monthly-collection",
"verbose_format": "Format état verbeux (utilisation des variables `date` et `days` (entre caractères accolade ))",
"date_format": "Format de date (voir http://strftime.org/)"
}
}
},
"error": {
"value": "Valeur invalide. Veuillez vérifier votre saisie !",
"icon": "Les icônes doivent être spécifiées dans le format 'prefix:name'.",
"days": "Choisir un ou plusieurs jours !",
"entities": "L'entité n'existe pas !",
"month_day": "Format de date invalide !",
"time": "Format d'heure invalide !",
"weekday_order_number": "Choisir un ou plusieurs jours",
"week_order_number": "Choisir une ou plusieurs semaines",
"period": "La semaine doit être un nombre entre 1 et 1000",
"first_week": "La première semaine doit être un nombre entre 1 et 52",
"date": "Format de date invalide !"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Raccolta Differenziata - Frequenza di raccolta (1/2)",
"description": "Immetti il nome del sensore e configura i suoi parametri. Maggiori informazioni su https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Nome personalizzato",
"hidden": "Nascondi nel calendario",
"frequency": "Frequenza",
"manual_update": "Stato del sensore aggiornato manualmente chiamando un servizio",
"icon_normal": "Icona (mdi:trash-can)",
"icon_tomorrow": "Icona raccolta domani (mdi:delete-restore)",
"icon_today": "Icona raccolta oggi (mdi:delete-circle)",
"expire_after": "Scade dopo (HH:MM) - opzionale",
"verbose_state": "Stato Verbale (testo, al posto di numeri)"
}
},
"detail": {
"title": "Raccolta Differenziata - Parametri aggiuntivi (2/2)",
"description": "Seleziona uno o più giorni di raccolta. Maggiori dettagli qui: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista di entità (separate da virgole)",
"collection_days": "Giorni di raccolta",
"first_month": "Prima raccolta del mese",
"last_month": "Ultima raccolta del mese",
"period": "Raccolta ogni n settimane/giorno: (1-1000)",
"first_week": "Prima raccolta della settimana (1-52)",
"first_date": "Prima date",
"weekday_order_number": "Ordine del giorno della settimana nel mese (ad esempio il primo mercoledì del mese)",
"force_week_order_numbers": "Ordine della settimana in un mese invece dell'ordine del giorno della settimana (per esempio il mercoledì della prima settimana)",
"verbose_format": "Formato Verbale (usare le variabili `date` e `days` (in parentesi graffe))",
"date_format": "Formato Data (vedere http://strftime.org/)"
}
}
},
"error": {
"value": "Valore non valido. Per favore controlla i tuoi input!",
"icon": "Le icone devono essere specificate nel formato 'prefix:name'.",
"days": "Seleziona uno o più giorni!",
"entities": "L'entità non esiste!",
"month_day": "Formato data non valido!",
"time": "Formato ora non valido!",
"weekday_order_number": "Seleziona uno o più giorni",
"week_order_number": "Seleziona uno o più settimani",
"period": "Il periodo deve essere un numero compreso tra 1 e 1000",
"first_week": "La prima settimana deve essere un numero tra 1 e 52",
"date": "Formato data non valido!"
},
"abort": {
"single_instance_allowed": "E' consentita solo una singola configurazione di Raccolta Differenziata."
}
},
"options": {
"step": {
"init": {
"title": "Raccolta Differenziata - Frequenza di raccolta (1/2)",
"description": "Cambia i parametri del sensore. Maggiori informazioni su https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Nascondi nel calendario",
"frequency": "Frequenza",
"manual_update": "Stato del sensore aggiornato manualmente chiamando un servizio",
"icon_normal": "Icona (mdi:trash-can)",
"icon_tomorrow": "Icona raccolta domani (mdi:delete-restore)",
"icon_today": "Icona raccolta oggi (mdi:delete-circle)",
"expire_after": "Scade dopo (HH:MM) - opzionale",
"verbose_state": "Stato Verbale (testo, al posto di numeri)"
}
},
"detail": {
"title": "Raccolta Differenziata - Parametri aggiuntivi (2/2)",
"description": "Seleziona uno o più giorni di raccolta. Maggiori dettagli qui: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista di entità (separate da virgole)",
"collection_days": "Giorni di raccolta",
"first_month": "Prima raccolta del mese",
"last_month": "Ultima raccolta del mese",
"period": "Raccolta ogni n settimane/giorno: (1-1000)",
"first_week": "Prima raccolta della settimana (1-52)",
"first_date": "Prima date",
"weekday_order_number": "Ordine del giorno della settimana nel mese (ad esempio il primo mercoledì del mese)",
"force_week_order_numbers": "Ordine della settimana in un mese invece dell'ordine del giorno della settimana (per esempio il mercoledì della prima settimana)",
"verbose_format": "Formato Verbale (usare le variabili `date` e `days` (in parentesi graffe))",
"date_format": "Formato Data (vedere http://strftime.org/)"
}
}
},
"error": {
"value": "Valore non valido. Per favore controlla i tuoi input!",
"icon": "Le icone devono essere specificate nel formato 'prefix:name'.",
"days": "Seleziona uno o più giorni!",
"entities": "L'entità non esiste!",
"month_day": "Formato data non valido!",
"time": "Formato ora non valido!",
"weekday_order_number": "Seleziona uno o più giorni",
"week_order_number": "Seleziona uno o più settimani",
"period": "Il periodo deve essere un numero compreso tra 1 e 1000",
"first_week": "La prima settimana deve essere un numero tra 1 e 52",
"date": "Formato data non valido!"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Wywóz Śmieci - Częstotliwość wywozu (1/2)",
"description": "Wprowadź nazwę dla sensora i skonfiguruj jego parametry. Więcej informacji na stronie https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Przyjazna nazwa",
"hidden": "Ukryj w kalendarzu",
"frequency": "Częstotliwość",
"manual_update": "Stan czujnika aktualizowany ręcznie poprzez wywołanie usługi",
"icon_normal": "Ikona (mdi:trash-can) - opcjonalnie",
"icon_tomorrow": "Ikona wywozu jutro (mdi:delete-restore) - opcjonalnie",
"icon_today": "Ikona wywozu dzisiaj (mdi:delete-circle) - opcjonalnie",
"expire_after": "Wygasają po (HH:MM) - opcjonalnie",
"verbose_state": "Tryb gadatliwy (tekst, zamiast liczb)"
}
},
"detail": {
"title": "Wywóz Śmieci - (2/2)",
"description": "Wybierz jeden lub więcej dni wywozu. Więcej szczegółów tutaj: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista encji (rozdzielane przecinkami)",
"collection_days": "Dni wywozu",
"first_month": "Miesiąc pierwszego wywozu",
"last_month": "Miesiąc ostatniego wywozu",
"period": "Wywóz co n dni/tygodni: (1-1000)",
"first_week": "Tydzień pierwszego wywozu: (1-52)",
"first_date": "Pierwsza data",
"weekday_order_number": "Kolejność dni tygodnia w miesiącu (np. pierwsza środa miesiąca)",
"force_week_order_numbers": "Kolejność tygodni w miesiącu zamiast kolejności dni tygodnia (np. w środę pierwszego tygodnia)",
"verbose_format": "Format trybu gadatliwego (używa zmiennych `date` oraz `days` (w nawiasach klamerkowych))",
"date_format": "Format daty (patrz http://strftime.org/)"
}
}
},
"error": {
"value": "Niewłaściwa wartość. Sprawdź swoje dane wejściowe!",
"icon": "Ikony powinny być określone w formacie 'prefix:name'.",
"days": "Wybierz 1 lub więcej dni!",
"entities": "Encja nie istnieje!",
"month_day": "Nieprawidłowy format daty!",
"time": "Nieprawidłowy format czasu!",
"weekday_order_number": "Wybierz 1 lub więcej dni!",
"week_order_number": "Wybierz 1 lub więcej tygodni!",
"period": "Okres musi być liczbą od 1 do 1000.",
"first_week": "Pierwszy tydzień musi być liczbą między 1 a 52.",
"date": "Nieprawidłowy format daty!"
},
"abort": {
"single_instance_allowed": "Tylko jedna konfiguracja dla wywozu śmieci jest dozwolona."
}
},
"options": {
"step": {
"init": {
"title": "Wywóz Śmieci - Częstotliwośc wywozu (1/2)",
"description": "Zmień parametry sensora. Więcej informacji tutaj https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Ukryj w kalendarzu",
"frequency": "Częstotliwość",
"manual_update": "Stan czujnika aktualizowany ręcznie poprzez wywołanie usługi",
"icon_normal": "Ikona (mdi:trash-can) - opcjonalnie",
"icon_tomorrow": "Ikona wywozu jutro (mdi:delete-restore) - opcjonalnie",
"icon_today": "Ikona wywozu dzisiaj (mdi:delete-circle) - opcjonalnie",
"expire_after": "Wygasają po (HH:MM) - opcjonalnie",
"verbose_state": "Tryb gadatliwy (tekst, zamiast liczb)"
}
},
"detail": {
"title": "Wywóz Śmieci - Dodatkowe parametry (2/2)",
"description": "Dołączone i wyłączone daty są listą, rozdzielanych przecinkami, dat w formacie rrrr-mm-dd. Więcej informacji tutaj https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista encji (rozdzielane przecinkami)",
"collection_days": "Dni wywozu",
"first_month": "Miesiąc pierwszego wywozu",
"last_month": "Miesiąc ostatniego wywozu",
"period": "Wywóz co n dni/tygodni: (1-1000)",
"first_week": "Tydzień pierwszego wywozu: (1-52)",
"first_date": "Pierwsza data",
"weekday_order_number": "Kolejność dni tygodnia w miesiącu (np. pierwsza środa miesiąca)",
"force_week_order_numbers": "Kolejność tygodni w miesiącu zamiast kolejności dni tygodnia (np. w środę pierwszego tygodnia)",
"verbose_format": "Format trybu gadatliwego (używa zmiennych `date` oraz `days` (w nawiasach klamrowych))",
"date_format": "Format daty (patrz http://strftime.org/)"
}
}
},
"error": {
"value": "Niewłaściwa wartość. Sprawdź swoje dane wejściowe!",
"icon": "Ikony powinny być określone w formacie 'prefix:name'.",
"days": "Wybierz 1 lub więcej dni!",
"entities": "Encja nie istnieje!",
"month_day": "Nieprawidłowy format daty!",
"time": "Nieprawidłowy format czasu!",
"weekday_order_number": "Wybierz 1 lub więcej dni!",
"week_order_number": "Wybierz 1 lub więcej tygodni!",
"period": "Okres musi być liczbą od 1 do 1000.",
"first_week": "Pierwszy tydzień musi być liczbą między 1 a 52.",
"date": "Nieprawidłowy format daty!"
}
}
}
@@ -0,0 +1,105 @@
{
"config": {
"step": {
"user": {
"title": "Coleta de lixo - Frequência de coleta (1/2)",
"description": "Insira o nome do sensor e configure os parâmetros do sensor. Mais informações em https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Nome fantasia",
"hidden": "Ocultar no calendário",
"frequency": "Frequência",
"manual_update": "Atualização manual - estado do sensor atualizado manualmente por um serviço (Blueprint)",
"icon_normal": "Ícone (mdi:trash-can) - opcional",
"icon_tomorrow": "ícones para coleta de amanhã (mdi:delete-restore) - opcional",
"icon_today": "Ícones para coleta de hoje (mdi:delete-circle) - opcional",
"expire_after": "Expirar depois (HH:MM) - opcional",
"verbose_state": "Estado detalhado (texto, em vez de número)"
}
},
"detail": {
"title": "Coleta de lixo - Parâmetros adicionais (2/2)",
"description": "Mais detalhes aqui: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista de entidades (separadas por vírgulas)",
"collection_days": "Dias de coleta",
"first_month": "Primeiro mês de coletah",
"last_month": "Último mês de coleta",
"period": "Coleta a cada n semanas/dias: (1-1000)",
"first_week": "Primeira semana de coleta (1-52)",
"first_date": "Primeira data",
"weekday_order_number": "Ordem do dia da semana no mês (por exemplo, primeira quarta-feira do mês)",
"force_week_order_numbers": "Ordem da semana em um mês em vez da ordem do dia da semana (por exemplo, na quarta-feira da primeira semana)",
"verbose_format": "Formato detalhado (usando variáveis `date` e `days` (entre colchetes))",
"date_format": "Formato de data (consulte http://strftime.org/)"
}
}
},
"error": {
"value": "Valor inválido. Por favor, verifique sua entrada!",
"icon": "Os ícones devem ser especificados no formato 'prefix:name'.",
"days": "Selecione 1 ou mais dias!",
"entities": "A entidade não existe!",
"month_day": "Formato de data inválido!",
"time": "Formato de hora inválido!",
"weekday_order_number": "Selecione 1 ou mais dias",
"week_order_number": "Selecione 1 ou mais semanas",
"period": "O período deve ser um número entre 1 e 1000",
"first_week": "A primeira semana deve ser um número entre 1 e 52",
"date": "Formato de data inválido!"
},
"abort": {
"single_instance_allowed": "Apenas uma única configuração de Coleta de Lixo é permitida."
}
},
"options": {
"step": {
"init": {
"title": "Coleta de lixo - Frequência de coleta (1/2)",
"description": "Altere os parâmetros do sensor. Mais informações em https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Ocultar no calendário",
"frequency": "Frequência",
"manual_update": "Atualização manual - estado do sensor atualizado manualmente por um serviço (Blueprint)",
"icon_normal": "Ícone (mdi:trash-can) - opcional",
"icon_tomorrow": "Ícones para coleta de amanhã (mdi:delete-restore) - opcional",
"icon_today": "Ícones para coleta de hoje (mdi:delete-circle) - opcional",
"expire_after": "Expirar depois (HH:MM) - opcional",
"verbose_state": "Estado detalhado (texto, em vez de número)"
}
},
"detail": {
"title": "Coleta de lixo - Parâmetros adicionais (2/2)",
"description": "Mais detalhes aqui: https://github.com/bruxy70/Garbage-Collection",
"data": {
"date": "Data (mm/dd)",
"entities": "Lista de entidades (separadas por vírgulas)",
"collection_days": "Dias de coleta",
"first_month": "Primeiro mês de coleta",
"last_month": "Último mês de coleta",
"period": "Coleta a cada n semanas/dias: (1-1000)",
"first_week": "Primeira semana de coleta (1-52)",
"first_date": "Primeira data",
"weekday_order_number": "Ordem do dia da semana no mês (por exemplo, primeira quarta-feira do mês)",
"force_week_order_numbers": "Ordem da semana em um mês em vez da ordem do dia da semana (por exemplo, na quarta-feira da primeira semana)",
"verbose_format": "Formato detalhado (usando variáveis `date` e `days` (entre colchetes))",
"date_format": "Formato de data (consulte http://strftime.org/)"
}
}
},
"error": {
"value": "Valor inválido. Por favor, verifique sua entrada!",
"icon": "Os ícones devem ser especificados no formato 'prefix:name'.",
"days": "Selecione 1 ou mais dias!",
"entities": "A entidade não existe!",
"month_day": "Formato de data inválido!",
"time": "Formato de hora inválido!",
"weekday_order_number": "Selecione 1 ou mais dias",
"week_order_number": "Selecione 1 ou mais semanas",
"period": "O período deve ser um número entre 1 e 1000",
"first_week": "A primeira semana deve ser um número entre 1 e 52",
"date": "Formato de data inválido!"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Dnes",
"tomorrow": "Zítra"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "I dag",
"tomorrow": "I morgen"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Heute",
"tomorrow": "Morgen"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Today",
"tomorrow": "Tomorrow"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Hoy",
"tomorrow": "Mañana"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Täna",
"tomorrow": "Homme"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Aujourd'hui",
"tomorrow": "Demain"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Oggi",
"tomorrow": "Domani"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Vandaag",
"tomorrow": "Morgen"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "I morgen",
"tomorrow": "I dag"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Dzisiaj",
"tomorrow": "Jutro"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Hoje",
"tomorrow": "Amanhã"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Idag",
"tomorrow": "Imorgon"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Dnes",
"tomorrow": "Zajtra"
}
}
}
@@ -0,0 +1,8 @@
{
"state": {
"garbage_collection__schedule": {
"today": "Danes",
"tomorrow": "Jutri"
}
}
}
@@ -0,0 +1,179 @@
{
"config": {
"step": {
"user": {
"title": "Garbage Collection - Nastavenia",
"description": "Zadej meno senzoru a nastav parametre. Viac na https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Friendly name",
"hidden": "Skryť v kalendári",
"frequency": "Frekvencia",
"icon_normal": "Ikona (mdi:trash-can)",
"icon_tomorrow": "Ikona zvoz zajtra (mdi:delete-restore)",
"icon_today": "Ikona zvoz dnes (mdi:delete-circle)",
"offset": "Odsadiť (medzi -31 a 31 dňami)",
"expire_after": "Čas expirácie (HH:MM)",
"verbose_state": "Verbose state (popis miesto čísiel)",
"verbose_format": "Verbose format (Použi `date` a `days` premenné (v zložených zátvorkách))",
"include_dates": "Pridané dátumy (yyyy-mm-dd, yyyy-mm-dd, ...) - voliteľné",
"exclude_dates": "Zakázané dátumy (yyyy-mm-dd, yyyy-mm-dd, ...) - voliteľné",
"date_format": "Formát data(see http://strftime.org/)"
}
},
"annual_group": {
"title": "Garbage Collection - Ďalšie parametre",
"description": "Zadej dátum zvozu (napr. 11/20 pre 20. november)",
"data": {
"date": "Dátum (mm/dd)",
"entities": "Zoznam entít (oddelené čiarkou)"
}
},
"detail": {
"title": "Garbage Collection - Dny svozu",
"description": "Vyber jeden alebo viacej dní zvozu. Viac na: https://github.com/bruxy70/Garbage-Collection",
"data": {
"collection_days_mon": "Pondelok",
"collection_days_tue": "Útorok",
"collection_days_wed": "Streda",
"collection_days_thu": "Štvrtok",
"collection_days_fri": "Piatok",
"collection_days_sat": "Sobota",
"collection_days_sun": "Nedeľa",
"force_week_order_numbers": "Číslo týždňa v mesiaci namiesto čísla dňa"
}
},
"final": {
"title": "Garbage Collection - Ďalšie parametre",
"description": "Pridané a zakázané dátumy sú zoznamy dátumov vo formáte yyyy-mm-dd, oddelených čiarkou. Viac info na https://github.com/bruxy70/Garbage-Collection",
"data": {
"first_month": "Prvý mesiac zvozu",
"last_month": "Posledný mesiac zvozu",
"period": "Perióda (zvoz každých n týždňov/dní): (1-1000)",
"first_week": "Prvý týždeň zvozu (1-52)",
"first_date": "Prvý dátum",
"weekday_order_number_1": "Prvý deň v mesiaci",
"weekday_order_number_2": "Druhý deň v mesiaci",
"weekday_order_number_3": "Tretí deň v mesiaci",
"weekday_order_number_4": "Štvrtý deň v mesiaci",
"weekday_order_number_5": "Piaty deň v mesiaci",
"week_order_number_1": "Prvý týžden v mesiaci",
"week_order_number_2": "Druhý týžden v mesiaci",
"week_order_number_3": "Tretí týžden v mesiaci",
"week_order_number_4": "Štvrtý týžden v mesiaci",
"week_order_number_5": "Piaty týžden v mesiaci",
"move_country_holidays": "Štátne sviatky - krajiny (voliteľné)",
"holiday_move_offset": "Posunúť sviatok o (dní: -7..7)",
"holiday_pop_named": "Ignorovať sviatky (volitelné)",
"holiday_in_week_move": "Posunúť na ďalší deň ak je dovolenka v týždni (voliteľné)",
"prov": "Štátne sviatky - provincie (volitelné)",
"state": "Štátne sviatky - štát (volitelné)",
"observed": "Štátne sviatky - sledované (volitelné)"
}
}
},
"error": {
"value": "Chybná hodnota. Skontroluj zadané hodnoty!",
"icon": "Ikony musia býť zadané vo formáte 'prefix:meno'.",
"days": "Vyber jeden nebo viacej dní!",
"entities": "Entita neexistuje!",
"month_day": "Zlý formát dátumu!",
"time": "Zlý formát času!",
"weekday_order_number": "Vyber jeden nebo viacej dní!",
"week_order_number": "Vyber jeden nebo viacej týždňov!",
"period": "Perióda musí být číslo medzi 1 a 1000",
"first_week": "Prvý týždeň musí být číslo medzi 1 a 52",
"date": "Zlý formát dátumu!"
},
"abort": {
"single_instance_allowed": "Je povolená iba jedna konfigurácia Garbage Collection."
}
},
"options": {
"step": {
"init": {
"title": "Garbage Collection - Nastavenia",
"description": "Zadaj meno senzora a nastav parametre. Viac na https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Skryť v kalendári",
"frequency": "Frekvencia",
"icon_normal": "Ikona (mdi:trash-can)",
"icon_tomorrow": "Ikona zvoz zajtra (mdi:delete-restore)",
"icon_today": "Ikona zvoz dnes (mdi:delete-circle)",
"offset": "Odsadit (medzi -31 a 31 dny)",
"expire_after": "Čas expirácie (HH:MM)",
"verbose_state": "Verbose state (popis miesto čísiel)",
"verbose_format": "Verbose format (Použi `date` a `days` premenné (v zložených zátvorkách))",
"include_dates": "Pridané dátumy (yyyy-mm-dd, yyyy-mm-dd, ...) - voliteľné",
"exclude_dates": "Zakázané dátumy (yyyy-mm-dd, yyyy-mm-dd, ...) - voliteľné",
"date_format": "Formát dátumu(see http://strftime.org/)"
}
},
"annual_group": {
"title": "Garbage Collection - Ďalšie parametre",
"description": "Zadej dátum zvozu (napr. 11/20 pre 20. november)",
"data": {
"date": "Dátum (mm/dd)",
"entities": "Zoznam entít (oddelené čiarkou)"
}
},
"detail": {
"title": "Garbage Collection - Dni zvozu",
"description": "Vyber jeden alebo viac dní zvozu. Více na: https://github.com/bruxy70/Garbage-Collection",
"data": {
"collection_days_mon": "Pondelok",
"collection_days_tue": "Útorok",
"collection_days_wed": "Streda",
"collection_days_thu": "Štvrtok",
"collection_days_fri": "Piatok",
"collection_days_sat": "Sobota",
"collection_days_sun": "Nedeľa",
"force_week_order_numbers": "Číslo týždna v mesiaci namiesto čísla dňa"
}
},
"final": {
"title": "Garbage Collection - Ďalšie parametre",
"description": "Pridané a zakázané dátumy sú zoznamy dátumov vo formáte yyyy-mm-dd, oddelených čiarkou. Viac info na https://github.com/bruxy70/Garbage-Collection",
"data": {
"first_month": "Prvý mesiac zvozu",
"last_month": "Posledný mesiac zvozu",
"period": "Perióda (zvoz každých n týždňov/dní): (1-1000)",
"first_week": "Prvý týždeň zvozu (1-52)",
"first_date": "Prvý dátum",
"weekday_order_number_1": "Prvý deň v mesiaci",
"weekday_order_number_2": "Druhý deň v mesiaci",
"weekday_order_number_3": "Tretí deň v mesiaci",
"weekday_order_number_4": "Štvrtý deň v mesiaci",
"weekday_order_number_5": "Piaty deň v mesiaci",
"week_order_number_1": "Prvý týžden v mesiaci",
"week_order_number_2": "Druhý týžden v mesiaci",
"week_order_number_3": "Tretí týžden v mesiaci",
"week_order_number_4": "Štvrtý týžden v mesiaci",
"week_order_number_5": "Piaty týžden v mesiaci",
"move_country_holidays": "Štátne sviatky - krajiny (voliteľné)",
"holiday_move_offset": "Posunúť sviatok o (dní: -7..7)",
"holiday_pop_named": "Ignorovať sviatky (volitelné)",
"holiday_in_week_move": "Posunúť na ďalší deň ak je dovolenka v týždni (voliteľné)",
"prov": "Štátne sviatky - provincie (volitelné)",
"state": "Štátne sviatky - štát (volitelné)",
"observed": "Štátne sviatky - sledované (volitelné)"
}
}
},
"error": {
"value": "Chybná hodnota. Skontroluj zadané hodnoty!",
"icon": "Ikony musia býť zadané vo formáte 'prefix:meno'.",
"days": "Vyber jeden nebo viacej dní!",
"entities": "Entita neexistuje!",
"month_day": "Zlý formát dátumu!",
"time": "Zlý formát času!",
"weekday_order_number": "Vyber jeden nebo viacej dní!",
"week_order_number": "Vyber jeden nebo viacej týždňov!",
"period": "Perióda musí být číslo medzi 1 a 1000",
"first_week": "Prvý týždeň musí být číslo medzi 1 a 52",
"date": "Zlý formát dátumu!"
}
}
}
@@ -0,0 +1,179 @@
{
"config": {
"step": {
"user": {
"title": "Odvoz odpadkov - Pogostost odvoza",
"description": "Vpišite ime senzorja in nastavite parametre. Več informacij na https://github.com/bruxy70/Garbage-Collection",
"data": {
"name": "Prijazno ime",
"hidden": "Skrij v koledarju",
"frequency": "Pogostost",
"manual_update": "Stanje senzorja se posodablja ročno s klicem storitve",
"icon_normal": "Ikona (mdi:trash-can) - opcijsko",
"icon_tomorrow": "Ikona pobiranja jutri (mdi:delete-restore) - opcijsko",
"icon_today": "Ikona pobiranja danes (mdi:delete-circle) - opcijsko",
"offset": "Zamik (med -31 in 31 dni)",
"expire_after": "Poteče po (HH:MM) - opcijsko",
"verbose_state": "Verbose stanje (tekst namesto številk)",
"verbose_format": "Verbose format (uporaba spremenljivk `datum` in `dni` (v oglatih oklepajih))",
"include_dates": "Vključi datume (yyyy-mm-dd, yyyy-mm-dd, ...) - opcijsko",
"exclude_dates": "Izključi datume (yyyy-mm-dd, yyyy-mm-dd, ...) - opcijsko",
"date_format": "Format datuma (glej http://strftime.org/)"
}
},
"annual_group": {
"title": "Odvoz odpadkov - Dodatni parametri",
"description": "Vpišite datum odvoza (npr. 11/20 za 20.November)",
"data": {
"date": "Datum (mm/dd)",
"entities": "Seznam vpisov (ločeno z vejico)"
}
},
"detail": {
"title": "Odvoz odpadkov - Collection days",
"description": "Izberite enega ali več dni odvoza. Več podrobnosti tukaj: https://github.com/bruxy70/Garbage-Collection",
"data": {
"collection_days_mon": "Ponedeljek",
"collection_days_tue": "Torek",
"collection_days_wed": "Sreda",
"collection_days_thu": "Četrtek",
"collection_days_fri": "Petek",
"collection_days_sat": "Sobota",
"collection_days_sun": "Nedelja",
"force_week_order_numbers": "Naročilo odvoza v tednu meseca namesto v dnevu tedna"
}
},
"final": {
"title": "Odvoz odpadkov - Dodatni parametri",
"description": "Vključeni in izključeni datumi so seznami datumov, ločeni z vejico v formatu yyyy-mm-dd. Podrobnosti na https://github.com/bruxy70/Garbage-Collection",
"data": {
"first_month": "Prvi mesec odvoza",
"last_month": "Zadnji mesec odvoza",
"period": "Odvoz vsakih v tednov/dni: (1-1000)",
"first_week": "Prvi teden odvoza (1-52)",
"first_date": "Prvi datum",
"weekday_order_number_1": "1. dan tedna v mesecu",
"weekday_order_number_2": "2. dan tedna v mesecu",
"weekday_order_number_3": "3. dan tedna v mesecu",
"weekday_order_number_4": "4. dan tedna v mesecu",
"weekday_order_number_5": "5. dan tedna v mesecu",
"week_order_number_1": "1. teden v mesecu",
"week_order_number_2": "2. teden v mesecu",
"week_order_number_3": "3. teden v mesecu",
"week_order_number_4": "4. teden v mesecu",
"week_order_number_5": "5. teden v mesecu",
"move_country_holidays": "Premakni na naslednji dan, če je praznik (opcijsko)",
"holiday_move_offset": "Premakni praznike za # dni(-7..7)",
"holiday_pop_named": "Ignoriraj praznike (seznam) - opcijsko",
"holiday_in_week_move": "Premakni na naslednji dan, če je praznik v tednu (opcijsko)",
"prov": "Državni prazniki - provincialni (opcijsko)",
"state": "Državni prazniki - državni (opcijsko)",
"observed": "Državni prazniki - premični (opcijsko)"
}
}
},
"error": {
"value": "Neveljavna vrednost. Prosim, preverite vaš vnos!",
"icon": "Ikone morajo biti vpisane v formatu 'predpona:ime'.",
"days": "Iizberite enega ali več dni!",
"entities": "Entiteta ne obstaja!",
"month_day": "Neveljaven format datuma!",
"time": "Neveljaven format časa!",
"weekday_order_number": "Izberite enega ali več dni",
"week_order_number": "Izberite enega ali več tednov",
"period": "Interval mora biti številka med 1 in 1000",
"first_week": "Prvi teden mora biti številka med 1 in 52",
"date": "Neveljaven format datuma!"
},
"abort": {
"single_instance_allowed": "Dovoljena je samo ena konfiguracija odvoza odpadkov."
}
},
"options": {
"step": {
"init": {
"title": "Odvoz odpadkov - Pogostost odvoza",
"description": "Spremenite parametre. Več informacij: https://github.com/bruxy70/Garbage-Collection",
"data": {
"hidden": "Skrij v koledarju",
"frequency": "Pogostost",
"manual_update": "Stanje senzorja se posodablja ročno s klicanjem storitve",
"icon_normal": "Ikona (mdi:trash-can) - opcijsko",
"icon_tomorrow": "Ikona codvoz jutri (mdi:delete-restore) - opcijsko",
"icon_today": "Ikona odvoz danes (mdi:delete-circle) - opcijsko",
"offset": "Zamik (med -31 in 31 dni)",
"expire_after": "Expire after (HH:MM) - opcijsko",
"verbose_state": "Verbose stanje (tekst namesto številk)",
"verbose_format": "Verbose format (uporaba spremenljivk `datum` in `dni` (v oglatih oklepajih))",
"include_dates": "Vključi datume (yyyy-mm-dd, yyyy-mm-dd, ...) - opcijsko",
"exclude_dates": "Izključi datume (yyyy-mm-dd, yyyy-mm-dd, ...) - opcijsko",
"date_format": "Format datume (glej http://strftime.org/)"
}
},
"annual_group": {
"title": "Odvoz odpadkov - Dodatni parametri",
"description": "Vpišite datum dneva odvoza (npr. 11/20 za 20.November)",
"data": {
"date": "Datum (mm/dd)",
"entities": "Seznam entitet (ločenih z vejivo)"
}
},
"detail": {
"title": "Odvoz odpadkov - Collection days",
"description": "Izberite enega ali več dni odvoza. Več informacij tukaj: https://github.com/bruxy70/Garbage-Collection",
"data": {
"collection_days_mon": "Ponedeljek",
"collection_days_tue": "Torek",
"collection_days_wed": "Sreda",
"collection_days_thu": "Četrtek",
"collection_days_fri": "Petek",
"collection_days_sat": "Sobota",
"collection_days_sun": "Nedelja",
"force_week_order_numbers": "Naročilo odvoza v tednu meseca namesto v dnevu tedna"
}
},
"final": {
"title": "Odvoz odpadkov - Dodatni parametri",
"description": "Vključeni in izključeni datumi so seznami datumov, ločeni z vejico v formatu yyyy-mm-dd. Podrobnosti na https://github.com/bruxy70/Garbage-Collection",
"data": {
"first_month": "Prvi mesec odvoza",
"last_month": "Zdnji mesec odvoza",
"period": "Odvoz vsakih n tednov/dni: (1-1000)",
"first_week": "Prvi teden odvoza (1-52)",
"first_date": "Prvi datum",
"weekday_order_number_1": "1. dan tedna v mesecu",
"weekday_order_number_2": "2. dan tedna v mesecu",
"weekday_order_number_3": "3. dan tedna v mesecu",
"weekday_order_number_4": "4. dan tedna v mesecu",
"weekday_order_number_5": "5. dan tedna v mesecu",
"week_order_number_1": "1. teden v mesecu",
"week_order_number_2": "2. teden v mesecu",
"week_order_number_3": "3. teden v mesecu",
"week_order_number_4": "4. teden v mesecu",
"week_order_number_5": "5. teden v mesecu",
"move_country_holidays": "Premakni praznik na naslednji dan (opcijsko)",
"holiday_move_offset": "Premakni praznike za # dni(-7..7)",
"holiday_pop_named": "Ignoriraj praznike (seznam) - opcijsko",
"holiday_in_week_move": "Premakni na naslednji dan, če je praznik v tednu (opcijsko)",
"prov": "Državni prazniki - provincialni (opcijsko)",
"state": "Državni prazniki- državni (opcijsko)",
"observed": "Državni prazniki - premični (opcijsko)"
}
}
},
"error": {
"value": "Neveljavna vrednost. Prosim, preverite vaš vnos!",
"icon": "Ikona mora biti vpisana v formatu 'predpona:ime'.",
"days": "Izberite enega ali več dni!",
"entities": "Entiteta ne obstaja!",
"month_day": "Neveljaven format datuma!",
"time": "Neveljaven format časa!",
"weekday_order_number": "Izberite enega ali več dni",
"week_order_number": "Izberite enega ali več tednov",
"period": "Interval mora biti številka med 1 in 1000",
"first_week": "Prvi teden mora biti številka med 1 in 52",
"date": "Neveljaven format datuma!"
}
}
}