Compare commits
11
Commits
2db2f294dc
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0694975103 | ||
|
|
bf22894f45 | ||
|
|
b5775a265d | ||
|
|
82a328bc4f | ||
|
|
2c9b18704d | ||
|
|
44629e625b | ||
|
|
ee5de1bd4d | ||
|
|
0dc49decbb | ||
|
|
35d6cf71dd | ||
|
|
6da3332a73 | ||
|
|
8c414c6660 |
+358
@@ -0,0 +1,358 @@
|
||||
.vscode/settings.json
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
##
|
||||
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||
|
||||
# User-specific files
|
||||
*.rsuser
|
||||
*.suo
|
||||
*.user
|
||||
*.userosscache
|
||||
*.sln.docstates
|
||||
|
||||
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||
*.userprefs
|
||||
|
||||
# Mono auto generated files
|
||||
mono_crash.*
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
[Ll]ogs/
|
||||
|
||||
# Visual Studio 2015/2017 cache/options directory
|
||||
.vs/
|
||||
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||
#wwwroot/
|
||||
|
||||
# Visual Studio 2017 auto generated files
|
||||
Generated\ Files/
|
||||
|
||||
# MSTest test Results
|
||||
[Tt]est[Rr]esult*/
|
||||
[Bb]uild[Ll]og.*
|
||||
|
||||
# NUnit
|
||||
*.VisualState.xml
|
||||
TestResult.xml
|
||||
nunit-*.xml
|
||||
|
||||
# Build Results of an ATL Project
|
||||
[Dd]ebugPS/
|
||||
[Rr]eleasePS/
|
||||
dlldata.c
|
||||
|
||||
# Benchmark Results
|
||||
BenchmarkDotNet.Artifacts/
|
||||
|
||||
# .NET Core
|
||||
project.lock.json
|
||||
project.fragment.lock.json
|
||||
artifacts/
|
||||
|
||||
# StyleCop
|
||||
StyleCopReport.xml
|
||||
|
||||
# Files built by Visual Studio
|
||||
*_i.c
|
||||
*_p.c
|
||||
*_h.h
|
||||
*.ilk
|
||||
*.meta
|
||||
*.obj
|
||||
*.iobj
|
||||
*.pch
|
||||
*.pdb
|
||||
*.ipdb
|
||||
*.pgc
|
||||
*.pgd
|
||||
*.rsp
|
||||
*.sbr
|
||||
*.tlb
|
||||
*.tli
|
||||
*.tlh
|
||||
*.tmp
|
||||
*.tmp_proj
|
||||
*_wpftmp.csproj
|
||||
*.log
|
||||
*.vspscc
|
||||
*.vssscc
|
||||
.builds
|
||||
*.pidb
|
||||
*.svclog
|
||||
*.scc
|
||||
|
||||
# Chutzpah Test files
|
||||
_Chutzpah*
|
||||
|
||||
# Visual C++ cache files
|
||||
ipch/
|
||||
*.aps
|
||||
*.ncb
|
||||
*.opendb
|
||||
*.opensdf
|
||||
*.sdf
|
||||
*.cachefile
|
||||
*.VC.db
|
||||
*.VC.VC.opendb
|
||||
|
||||
# Visual Studio profiler
|
||||
*.psess
|
||||
*.vsp
|
||||
*.vspx
|
||||
*.sap
|
||||
|
||||
# Visual Studio Trace Files
|
||||
*.e2e
|
||||
|
||||
# TFS 2012 Local Workspace
|
||||
$tf/
|
||||
|
||||
# Guidance Automation Toolkit
|
||||
*.gpState
|
||||
|
||||
# ReSharper is a .NET coding add-in
|
||||
_ReSharper*/
|
||||
*.[Rr]e[Ss]harper
|
||||
*.DotSettings.user
|
||||
|
||||
# TeamCity is a build add-in
|
||||
_TeamCity*
|
||||
|
||||
# DotCover is a Code Coverage Tool
|
||||
*.dotCover
|
||||
|
||||
# AxoCover is a Code Coverage Tool
|
||||
.axoCover/*
|
||||
!.axoCover/settings.json
|
||||
|
||||
# Visual Studio code coverage results
|
||||
*.coverage
|
||||
*.coveragexml
|
||||
|
||||
# NCrunch
|
||||
_NCrunch_*
|
||||
.*crunch*.local.xml
|
||||
nCrunchTemp_*
|
||||
|
||||
# MightyMoose
|
||||
*.mm.*
|
||||
AutoTest.Net/
|
||||
|
||||
# Web workbench (sass)
|
||||
.sass-cache/
|
||||
|
||||
# Installshield output folder
|
||||
[Ee]xpress/
|
||||
|
||||
# DocProject is a documentation generator add-in
|
||||
DocProject/buildhelp/
|
||||
DocProject/Help/*.HxT
|
||||
DocProject/Help/*.HxC
|
||||
DocProject/Help/*.hhc
|
||||
DocProject/Help/*.hhk
|
||||
DocProject/Help/*.hhp
|
||||
DocProject/Help/Html2
|
||||
DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
*.azurePubxml
|
||||
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||
# but database connection strings (with potential passwords) will be unencrypted
|
||||
*.pubxml
|
||||
*.publishproj
|
||||
|
||||
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||
# in these scripts will be unencrypted
|
||||
PublishScripts/
|
||||
|
||||
# NuGet Packages
|
||||
*.nupkg
|
||||
# NuGet Symbol Packages
|
||||
*.snupkg
|
||||
# The packages folder can be ignored because of Package Restore
|
||||
**/[Pp]ackages/*
|
||||
# except build/, which is used as an MSBuild target.
|
||||
!**/[Pp]ackages/build/
|
||||
# Uncomment if necessary however generally it will be regenerated when needed
|
||||
#!**/[Pp]ackages/repositories.config
|
||||
# NuGet v3's project.json files produces more ignorable files
|
||||
*.nuget.props
|
||||
*.nuget.targets
|
||||
|
||||
# Microsoft Azure Build Output
|
||||
csx/
|
||||
*.build.csdef
|
||||
|
||||
# Microsoft Azure Emulator
|
||||
ecf/
|
||||
rcf/
|
||||
|
||||
# Windows Store app package directories and files
|
||||
AppPackages/
|
||||
BundleArtifacts/
|
||||
Package.StoreAssociation.xml
|
||||
_pkginfo.txt
|
||||
*.appx
|
||||
*.appxbundle
|
||||
*.appxupload
|
||||
|
||||
# Visual Studio cache files
|
||||
# files ending in .cache can be ignored
|
||||
*.[Cc]ache
|
||||
# but keep track of directories ending in .cache
|
||||
!?*.[Cc]ache/
|
||||
|
||||
# Others
|
||||
ClientBin/
|
||||
~$*
|
||||
*~
|
||||
*.dbmdl
|
||||
*.dbproj.schemaview
|
||||
*.jfm
|
||||
*.pfx
|
||||
*.publishsettings
|
||||
orleans.codegen.cs
|
||||
|
||||
# Including strong name files can present a security risk
|
||||
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
|
||||
#*.snk
|
||||
|
||||
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||
#bower_components/
|
||||
|
||||
# RIA/Silverlight projects
|
||||
Generated_Code/
|
||||
|
||||
# Backup & report files from converting an old project file
|
||||
# to a newer Visual Studio version. Backup files are not needed,
|
||||
# because we have git ;-)
|
||||
_UpgradeReport_Files/
|
||||
Backup*/
|
||||
UpgradeLog*.XML
|
||||
UpgradeLog*.htm
|
||||
ServiceFabricBackup/
|
||||
*.rptproj.bak
|
||||
|
||||
# SQL Server files
|
||||
*.mdf
|
||||
*.ldf
|
||||
*.ndf
|
||||
|
||||
# Business Intelligence projects
|
||||
*.rdl.data
|
||||
*.bim.layout
|
||||
*.bim_*.settings
|
||||
*.rptproj.rsuser
|
||||
*- [Bb]ackup.rdl
|
||||
*- [Bb]ackup ([0-9]).rdl
|
||||
*- [Bb]ackup ([0-9][0-9]).rdl
|
||||
|
||||
# Microsoft Fakes
|
||||
FakesAssemblies/
|
||||
|
||||
# GhostDoc plugin setting file
|
||||
*.GhostDoc.xml
|
||||
|
||||
# Node.js Tools for Visual Studio
|
||||
.ntvs_analysis.dat
|
||||
node_modules/
|
||||
|
||||
# Visual Studio 6 build log
|
||||
*.plg
|
||||
|
||||
# Visual Studio 6 workspace options file
|
||||
*.opt
|
||||
|
||||
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||
*.vbw
|
||||
|
||||
# Visual Studio LightSwitch build output
|
||||
**/*.HTMLClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/GeneratedArtifacts
|
||||
**/*.DesktopClient/ModelManifest.xml
|
||||
**/*.Server/GeneratedArtifacts
|
||||
**/*.Server/ModelManifest.xml
|
||||
_Pvt_Extensions
|
||||
|
||||
# Paket dependency manager
|
||||
.paket/paket.exe
|
||||
paket-files/
|
||||
|
||||
# FAKE - F# Make
|
||||
.fake/
|
||||
|
||||
# CodeRush personal settings
|
||||
.cr/personal
|
||||
|
||||
# Python Tools for Visual Studio (PTVS)
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Cake - Uncomment if you are using it
|
||||
# tools/**
|
||||
# !tools/packages.config
|
||||
|
||||
# Tabs Studio
|
||||
*.tss
|
||||
|
||||
# Telerik's JustMock configuration file
|
||||
*.jmconfig
|
||||
|
||||
# BizTalk build output
|
||||
*.btp.cs
|
||||
*.btm.cs
|
||||
*.odx.cs
|
||||
*.xsd.cs
|
||||
|
||||
# OpenCover UI analysis results
|
||||
OpenCover/
|
||||
|
||||
# Azure Stream Analytics local run output
|
||||
ASALocalRun/
|
||||
|
||||
# MSBuild Binary and Structured Log
|
||||
*.binlog
|
||||
|
||||
# NVidia Nsight GPU debugger configuration file
|
||||
*.nvuser
|
||||
|
||||
# MFractors (Xamarin productivity tool) working folder
|
||||
.mfractor/
|
||||
|
||||
# Local History for Visual Studio
|
||||
.localhistory/
|
||||
|
||||
# BeatPulse healthcheck temp database
|
||||
healthchecksdb
|
||||
|
||||
# Backup folder for Package Reference Convert tool in Visual Studio 2017
|
||||
MigrationBackup/
|
||||
|
||||
# Ionide (cross platform F# VS Code tools) working folder
|
||||
.ionide/
|
||||
|
||||
# Virtual Environment
|
||||
venv/
|
||||
|
||||
# Mac OS
|
||||
*.DS_Store
|
||||
@@ -1,514 +1,138 @@
|
||||
[](https://github.com/hacs/integration) [](https://github.com/bruxy70/Garbage-Collection) 
|
||||
[](https://github.com/hacs/integration)
|
||||
[](https://github.com/bruxy70/Garbage-Collection)
|
||||

|
||||
|
||||
## Fork notes
|
||||
# ♻️ Garbage Collection (fork FR)
|
||||
|
||||
This is a personal fork of [bruxy70/Garbage-Collection](https://github.com/bruxy70/Garbage-Collection) (original project archived/unmaintained since end of 2022).
|
||||
Cette intégration Home Assistant permet de créer des capteurs personnalisés pour gérer les calendriers de collecte des déchets.
|
||||
|
||||
Added on top of the original: a built-in **"Décaler la collecte si jour férié dans la semaine"** option (config + options flow, both steps), available for every frequency. When enabled, it uses the [`holidays`](https://pypi.org/project/holidays/) Python library (no separate calendar entity needed) for the configured country code (`holiday_country`, default `FR`). For each calculated collection date, if a public holiday falls on that day or any earlier day in the same ISO week, the collection date is pushed forward by one day, cascading if the new day also lands on a holiday. This reproduces the logic of the original project's `holiday_in_week` blueprint, but built directly into the integration so no separate blueprint or external "Holidays" helper is required.
|
||||
---
|
||||
|
||||
## End of Support (upstream)
|
||||
## 🍴 Fork personnel
|
||||
|
||||
Home Assistant has introduced local calendars in 2022, in the 2023.1 release they added an option for different recurent events. With this, most of the functionality of this custom helper is supported natively. So I will end developing and supporting this helper in 2023.
|
||||
Ce dépôt est un **fork personnel** de :
|
||||
https://github.com/bruxy70/Garbage-Collection
|
||||
|
||||
## Table of Contents
|
||||
L’intégration originale n’est plus activement maintenue depuis fin 2022.
|
||||
|
||||
- [Description](#garbage-collection)
|
||||
- [Installation](#installation)
|
||||
---
|
||||
|
||||
- [Manual Installation](#manual-installation)
|
||||
- [Installation via Home Assistant Community Store (HACS)](#installation-via-home-assistant-community-store-hacs)
|
||||
## ✨ Modifications apportées dans ce fork
|
||||
|
||||
- [Configuration](#configuration)
|
||||
- [Blueprints for Manual Update](#blueprints-for-manual-update)
|
||||
- [Public Holidays](#public-holidays)
|
||||
- [Include and Exclude](#include-and-exclude)
|
||||
- [Offset](#offset)
|
||||
- [Import TXT](#import-txt)
|
||||
- [Monthly on a fixed date](#monthly-on-a-fixed-date)
|
||||
- [State and Attributes](#state-and-attributes)
|
||||
- [Lovelace configuration examples](#lovelace-config-examples)
|
||||
### 🇫🇷 Gestion intégrée des jours fériés
|
||||
|
||||
# Garbage Collection
|
||||
Ajout d’une fonctionnalité native permettant de décaler automatiquement les collectes si un jour férié est détecté dans la semaine.
|
||||
|
||||
The `garbage_collection` component is a Home Assistant helper that creates a custom sensor for monitoring a regular garbage collection schedule. The sensor can be configured for a number of different patterns:
|
||||
- Utilise la librairie Python `holidays`
|
||||
- Aucun blueprint externe requis
|
||||
- Fonctionne avec un code pays (par défaut : FR)
|
||||
- Analyse la semaine ISO complète
|
||||
- Décale la collecte d’un jour si nécessaire (avec propagation si jours fériés consécutifs)
|
||||
|
||||
- `weekly` schedule (including multiple collection days, e.g. on Tuesday and Thursday)
|
||||
- `every-n-weeks` repeats every `period` of weeks, starting from the week number `first_week`. It uses the week number - therefore, it restarts each year, as the weeks start from number 1 each year.
|
||||
- bi-weekly in `even-weeks` or `odd-weeks` (technically, it is the same as every 2 weeks with 1<sup>st</sup> or 2<sup>nd</sup> `first_week`)
|
||||
- `every-n-days` (repeats regularly from the given first date). If n is a multiply of 7, it works similar to `every-n-weeks`, with the difference that it does not use the week numbers (that restart each year) but continues infinitely from the initial date.
|
||||
- `monthly` schedule (n<sup>th</sup> weekday each month), or a specific weekday of each n<sup>th</sup> week. Using the `period` it could also be every 2<sup>nd</sup>, 3<sup>rd</sup> etc month.
|
||||
- `annually` (e.g. birthdays). This is once per year. Using include dates, you can add additional dates manually.
|
||||
- `blank` does not automatically schedule any collections - to be used in cases where you want to make completely own schedule with `manual_update`.
|
||||
---
|
||||
|
||||
You can also configure seasonal calendars (e.g. for bio-waste collection), by configuring the first and last month.
|
||||
And you can `group` entities, which will merge multiple schedules into one sensor.
|
||||
## ⚠️ Compatibilité Home Assistant
|
||||
|
||||
These are some examples using this sensor. The Lovelace config examples are included below.
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/picture-entity.png">
|
||||
Compatible Home Assistant récent (2024+ / 2026)
|
||||
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/entities.png">
|
||||
Corrections incluses :
|
||||
- remplacement de `async_forward_entry_setup` (obsolète)
|
||||
- utilisation de `async_forward_entry_setups`
|
||||
- support du reload dynamique des options
|
||||
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/sensor.png">
|
||||
---
|
||||
|
||||
<img src="https://github.com/amaximus/garbage-collection-card/blob/master/garbage_collection_lovelace.jpg">
|
||||
## 📦 Installation
|
||||
|
||||
## Installation
|
||||
### Installation manuelle
|
||||
|
||||
### MANUAL INSTALLATION
|
||||
1. Copier le dossier :
|
||||
2.
|
||||
2. Redémarrer Home Assistant
|
||||
3. Ajouter l’intégration via l’interface
|
||||
|
||||
1. Download the
|
||||
[latest release](https://github.com/bruxy70/garbage_collection/releases/latest).
|
||||
2. Unpack the release and copy the `custom_components/garbage_collection` directory
|
||||
into the `custom_components` directory of your Home Assistant
|
||||
installation.
|
||||
3. Restart Home Assistant.
|
||||
4. Configure the `garbage_collection` helper.
|
||||
---
|
||||
|
||||
### INSTALLATION VIA Home Assistant Community Store (HACS)
|
||||
### Installation via HACS
|
||||
|
||||
1. Ensure that [HACS](https://hacs.xyz/) is installed.
|
||||
2. Search for and install the "Garbage Collection" integration.
|
||||
3. Restart Home Assistant.
|
||||
4. Configure the `garbage_collection` helper.
|
||||
1. Ajouter ce dépôt dans HACS (Custom repository)
|
||||
2. Installer “Garbage Collection”
|
||||
3. Redémarrer Home Assistant
|
||||
4. Configurer via l’interface
|
||||
|
||||
## Configuration
|
||||
---
|
||||
|
||||
Go to `Settings`/`Devices & Services`/`Helpers`, click on the `+ CREATE HELPER` button, select `Garbage Collection` and configure the helper.<br />If you would like to add more than one collection schedule, click on the `+ CREATE HELPER` button again and add another `Garbage Collection` helper instance.
|
||||
## ⚙️ Configuration
|
||||
|
||||
**The configuration hapend in 2 steps.** In the first step, you select the `frequency` and common parameters. In the second step you configure additional parameters depending on the selected frequency.
|
||||
Configuration via :
|
||||
Paramètres → Appareils et services → Ajouter un assistant (Helper)
|
||||
|
||||
_The configuration via `configuration.yaml` has been deprecated. If you have previously configured the integration there, it will be imported to ConfigFlow, and you should remove it._
|
||||
---
|
||||
|
||||
### STEP 1 - Common Parameters
|
||||
## 🧩 Fonctionnalités
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :------------------ | :------- | :----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Friendly name` | Yes | Sensor friendly name |
|
||||
| `Frequency` | Yes | `"weekly"`, `"even-weeks"`, `"odd-weeks"`, `"every-n-weeks"`, `"every-n-days"`, `"monthly"`, `"annual"`, `"group"` or `"blank"` |
|
||||
| `Icon` | No | Default icon **Default**: `mdi:trash-can` |
|
||||
| `Icon today` | No | Icon if the collection is today **Default**: `mdi:delete-restore` |
|
||||
| `Icon tomorrow` | No | Icon if the collection is tomorrow **Default**: `mdi:delete-circle` |
|
||||
| `Expire After` | No | Time in format format `HH:MM`. If the collection is due today, start looking for the next occurence after this time (i.e. if the weekly collection is in the morning, change the state from 'today' to next week in the afternoon) |
|
||||
| `Verbose state` | No | The sensor state will show collection date and remaining days, instead of number.<br/>**Obsolete** - do the formatting on the dashboard instead.<br/>**Default**: `False` |
|
||||
| `Hidde in calendar` | No | Hide in calendar (useful for sensors that are used in groups)<br/>**Default**: `False` |
|
||||
| `Manual update` | No | (Advanced). Do not automatically update the status. Status is updated manualy by calling the service `garbage_collection.update_state` from an automation triggered by event `garbage_collection_loaded`, that could manually add or remove collection dates, and manually trigger the state update at the end. [See the example](#manual-update-examples).</br>**Default**: `False` |
|
||||
| `Verbose format` | No | (relevant when `verbose state` is `True`). Verbose status formatting string. Can use placeholders `{date}` and `{days}` to show the date of next collection and remaining days.<br/>**Obsolete** - do the formatting on the dashboard instead.<br />**Default**: `'on {date}, in {days} days'`</br>_When the collection is today or tomorrow, it will show `Today` or `Tomorrow`_</br>_(currently in English, French, Czech and Italian)._ |
|
||||
| `Date format` | No | In the `verbose format`, you can configure the format of date (using [strftime](http://strftime.org/) format)<br/>**Obsolete** - do the formatting on the dashboard instead.<br />**Default**: `'%d-%b-%Y'` |
|
||||
- 📅 Hebdomadaire
|
||||
- 🔁 Toutes les X semaines
|
||||
- 📆 Tous les X jours
|
||||
- 🗓️ Mensuel (n-ième semaine ou jour du mois)
|
||||
- 🎂 Annuel
|
||||
- 🔗 Groupement de capteurs
|
||||
- ✋ Mode manuel avancé
|
||||
|
||||
### STEP 2 - parameters depending on the selected frequency
|
||||
---
|
||||
|
||||
#### ...FOR ALL FREQUENCIES EXCEPT ANNUAL, GROUP and BLANK
|
||||
## 🧪 États du capteur
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :------------ | :------- | :--------------------------------------------------------------------------------- |
|
||||
| `First month` | No | Month three letter abbreviation, e.g. `"jan"`, `"feb"`...<br/>**Default**: `"jan"` |
|
||||
| `Last month` | No | Month three letter abbreviation.<br/>**Default**: `"dec"` |
|
||||
| État | Signification |
|
||||
|------|--------------|
|
||||
| 0 | Collecte aujourd’hui |
|
||||
| 1 | Collecte demain |
|
||||
| 2 | Collecte plus tard |
|
||||
|
||||
#### ...FOR ALL FREQUENCIES EXCEPT ANNUAL, EVERY-N-DAYS, GROUP and BLANK
|
||||
---
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :---------------- | :------- | :---------------------------------------------------------------------------------------------------- |
|
||||
| `Collection days` | Yes | Day three letter abbreviation, list of `"mon"`, `"tue"`, `"wed"`, `"thu"`, `"fri"`, `"sat"`, `"sun"`. |
|
||||
## 📊 Attributs
|
||||
|
||||
#### ...FOR COLLECTION EVERY-N-WEEKS
|
||||
- `next_date` → prochaine collecte
|
||||
- `days` → jours restants
|
||||
- `last_collection` → dernière collecte
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :----------- | :------- | :-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Period` | No | Collection every `"period"` weeks (integer 1-53)<br/>**Default**: 1 |
|
||||
| `First week` | No | First collection on the `"first week"` week (integer 1-53)<br/>**Default**: 1<br/>_(The week number is using [ISO-8601](https://en.wikipedia.org/wiki/ISO_8601#Week_dates) numeric representation of the week)<br/><br/>Note: This parameter cannot be used to set the beginning of the collection period (use the `first month` parameter for that). The purpose of `first week` is to simply 'offset' the week number, so the collection every ;'n' weeks does not always trigger on week numbers that are multiplication of 'n'. Technically, the value of this parameter shall be less than `period`, otherwise it will give weird results. Also note, that the week numbers restart each year. Use `every-n-days` frequency if you need a consistent period across the year ends._ |
|
||||
---
|
||||
|
||||
#### ...FOR COLLECTION EVERY-N-DAYS
|
||||
## 🛠️ Services
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :----------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `First date` | Yes | Repeats every n days from this first date<br/>(date in the international ISO format `'yyyy-mm-dd'`). |
|
||||
| `Period` | No | Collection every `"period"` days (warning - in this configuration, it is days, not weeks!)<br/>**Default**: 1 (daily, which makes no sense I suppose) |
|
||||
- `garbage_collection.collect_garbage`
|
||||
- `garbage_collection.update_state`
|
||||
- `garbage_collection.add_date`
|
||||
- `garbage_collection.remove_date`
|
||||
- `garbage_collection.offset_date`
|
||||
|
||||
#### ...FOR MONTHLY COLLECTION
|
||||
---
|
||||
|
||||
The monthly schedule has two flavors: it can trigger either on the **n<sup>th</sup> occurrence of the weekday** in a month, or on the weekday in the **n<sup>th</sup> week** of each month.
|
||||
## 🧠 Mode avancé
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :---------------------------------------- | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `Order of weekday` | Yes | List of week numbers of `collection day` each month. E.g., if `collection_day` is `"sat"`, 1 will mean 1<sup>st</sup> Saturday each month (integer 1-5) |
|
||||
| `Order of week, instead of weekday order` | No | **CONFIGURE THIS ONE ONLY IF YOU ARE SURE YOU NEED IT**. This will **alter** the behaviour of `order of weekday`, so that instead of n<sup>th</sup> weekday of each month, take the weekday of the n<sup>th</sup> week of each month.</br>So if the month starts on Friday, the Wednesday of the 1<sup>st</sup> week would actually be last Wednesday of the previous month and the Wednesday of 2<sup>nd</sup> week will be the 1<sup>st</sup> Wednesday of the month. So if you have just randomy clicked on the option, it might appear as if it calculates a wrong date! Yes, this is confusing, but there are apparently some use case for this. |
|
||||
| `Period` | No | If `period` is not defined (or 1), the schedule will repeat monthly. If `period` is 2, it will be every 2<sup>nd</sup> month. If `period` is 3, it will be once per quarter, and so on.<br/>The `first month` parameter will then define the starting month. So if the `first month` is `jan` (or not defined), and `period` is 2, the collection will be in odd months (`jan`, `mar`, `may`, `jul`, `sep` and `nov`). If `first month` is `feb`, it will be in even months. (integer 1-12)<br/>**Default**: 1 |
|
||||
Permet des règles personnalisées via automations Home Assistant.
|
||||
|
||||
#### ...FOR ANNUAL COLLECTION
|
||||
⚠️ réservé aux utilisateurs avancés
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :-------- | :------- | :----------------------------------------------------------------------------------- |
|
||||
| `Date` | Yes | The date of collection, in format `'mm/dd'` (e.g. '11/24' for November 24 each year) |
|
||||
---
|
||||
|
||||
#### ...FOR GROUP
|
||||
## 📅 Jours fériés
|
||||
|
||||
| Parameter | Required | Description |
|
||||
| :----------------- | :------- | :------------------------------ |
|
||||
| `List of entities` | Yes | A list of `entity_id`s to merge |
|
||||
- détection automatique
|
||||
- décalage des collectes
|
||||
- configuration par pays (`holiday_country`)
|
||||
|
||||
## Blueprints for Manual Update
|
||||
---
|
||||
|
||||
### Prerequisites
|
||||
## ❤️ Remarque
|
||||
|
||||
1. To use the **blueprints**, you need to set the `garbage_collection` entity for `Manual update`, that will fire the `garbage_collection_loaded` event on each sensor update and trigger the automation **blueprint**.
|
||||
2. Install/Import **blueprint**
|
||||
3. From the **blueprint**, create and configure the automation
|
||||
Ce fork est personnel et expérimental.
|
||||
Il peut diverger du projet original.
|
||||
|
||||
### Public Holidays
|
||||
---
|
||||
|
||||
There are a couple of **blueprints**, automatically moving the collection falling on a public holiday. Or if there was a public holiday in the week before the scheduled collection.
|
||||
## 📜 Licence
|
||||
|
||||
The Public Holidays **blueprints** use a separate custom helper **Holidays**, available through **HACS**, that you can configure for different countries.
|
||||
|
||||
| <!-- --> | <!-- --> |
|
||||
| :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmove_on_holiday.yaml) | Move the collection to the next day, if the collection falls on public holiday |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmove_on_holiday_with_include_exclude.yaml) | Remove events falling on provided "exclude" list of dates. Then check the calendar of public holidays and move events that fall on a public holiday to the next day. Finally, add additional events on dates from "include" list. |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fholiday_in_week.yaml) | Move forward one day if a public holiday was in the collection week, before or on the collection day (and keep moving if the new collection day also falls on a holiday) |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmultiple_holidays_in_week.yaml) | Move forward by a day for each public holiday was in the collection week, before or on the collection day (and keep moving if the new collection day also falls on a holiday). So if there were two public holidays, move by two days. |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmove_on_holiday_carry_over.yaml) | Move forward by one day if there was a public holiday in the collection week, before or on the collection day (and keep moving if the new collection day also falls on a holiday). Only move by one day, but if there was more than one public holiday in the week, carry it over to the following week. So if there were 2 public holidays this week, move it by one day this week and one day next week. |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fskip_holday.yaml) | Skip the holiday |
|
||||
|
||||
### Include and Exclude
|
||||
|
||||
A list of fixed dates to include and exclude from the calculated schedule.
|
||||
|
||||
| <!-- --> | <!-- --> |
|
||||
| :------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | :------------------ |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Finclude_exclude.yaml) | Include and Exclude |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Finclude.yaml) | Include |
|
||||
| [](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fexclude.yaml) | Exclude |
|
||||
|
||||
### Offset
|
||||
|
||||
The offset blueprint will move the calculated collections by a number of days. This can be used, for example, to schedule collection for last Saturday each month - just set the collection to the first Saturday each month and offset it by -7 days.
|
||||
|
||||
[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Foffset.yaml)
|
||||
|
||||
### Import txt
|
||||
|
||||
This **blueprint** requires a `command_line` sensor reading content of a txt file, containig a set of dates, one per line.
|
||||
|
||||
[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fimport_txt.yaml)
|
||||
|
||||
<details>
|
||||
<summary>command_line sensor example</summary>
|
||||
|
||||
The Home Assistant command line sensor has a 255 character limitation. For many people this causes an issue where the sensor only imports part of the list. To overcome this limitation, @stu1811 came up with this brilliant solution.
|
||||
|
||||
Create a script that shows next 20 dates from the current day forward.
|
||||
|
||||
#### **`/share/import_dates.sh`**
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
new_dates=$(for x in $(cat /share/import_dates.txt); do
|
||||
if [[ $(date -d $x +"%y%m%d") -ge $(date +"%y%m%d") ]]; then
|
||||
echo $x
|
||||
fi
|
||||
done)
|
||||
echo "$new_dates" | head -n20
|
||||
```
|
||||
|
||||
Then, create the command line sensor taking the output of this script.
|
||||
|
||||
#### **`configuration.yaml`**
|
||||
|
||||
```yaml
|
||||
sensor:
|
||||
- platform: command_line
|
||||
name: Import dates
|
||||
command: "sh /share/import_dates.sh"
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Monthly on a fixed date
|
||||
|
||||
This will create a schedule on a fixed date each month. For example on the 3rd each month. The helper does not allow it, as it is generally designed around paterns evolving around weekly schedules (since garbage collection typically happens on a set day in a week, rather than set day in a month). But few of you wanted that, so here you go.
|
||||
|
||||
[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmonthly_fixed_date.yaml) One fixed date
|
||||
|
||||
[](https://my.home-assistant.io/redirect/blueprint_import/?blueprint_url=https%3A%2F%2Fgithub.com%2Fbruxy70%2FGarbage-Collection%2Fblob%2Fmaster%2Fblueprints%2Fmonthly_fixed_two_dates.yaml) Two dates
|
||||
|
||||
## STATE AND ATTRIBUTES
|
||||
|
||||
### State
|
||||
|
||||
The state can be one of
|
||||
|
||||
| Value | Meaning |
|
||||
| :---- | :--------------------- |
|
||||
| `0` | Collection is today |
|
||||
| `1` | Collection is tomorrow |
|
||||
| `2` | Collection is later |
|
||||
|
||||
If the `verbose_state` parameter is set, it will show the date, and the remaining days. For example: "Today" or "Tomorrow" or "on 10-Sep-2019, in 2 days" (configurable)
|
||||
|
||||
### Attributes
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------------- | :--------------------------------------- |
|
||||
| `next_date` | The date of next collection |
|
||||
| `days` | Days till the next collection |
|
||||
| `last_collection` | The date and time of the last collection |
|
||||
|
||||
## Services
|
||||
|
||||
### `garbage_collection.collect_garbage`
|
||||
|
||||
If the collection is scheduled for today, mark it completed and look for the next collection.
|
||||
It will set the `last_collection` attribute to the current date and time.
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
| `last_collection` | (optional) Set the last collection date to this value. This can be used to re-set the next collection calculation, if the last collection date was set in error. If omitted, it will set the last collection to the current date & time. |
|
||||
|
||||
## Manual update
|
||||
|
||||
There are standard [blueprints](#blueprints-for-manual-update) provided to handle manual updates - to move collection on public holidays or offset the collection.
|
||||
|
||||
If these **blueprints** do not work for you, you can create your own custom rules to handle any scenario. If you do so, please share the blueprints with the others by posting them to the [blueprints directory](https://github.com/bruxy70/Garbage-Collection/tree/master/blueprints) - someone else might find them useful. Thanks!
|
||||
To help you with creating custom automations, see the following examples:
|
||||
|
||||
## _!!! Advanced !!! If you think this is too complicated, then this is not for you!!!_
|
||||
|
||||
<details>
|
||||
|
||||
## Services used for `manual_update`
|
||||
|
||||
The following services are used within automations, triggered by the [garbage_collection_loaded](#garbage_collection_loaded) event. Don't use them anywhere else, it won't work. For the examples of their use, see the [examples](#manual-update-examples)
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
A[HA updates entity] -->B[triggered garbage_collection_loaded event]
|
||||
B --> C[calling services]
|
||||
C --> D[garbage_collection.add_date] & E[garbage_collection.remove_date] & F[garbage_collection.offset_date] --> C
|
||||
D & E & F --> G[manual update finished -> garbage_collection.update_state]
|
||||
```
|
||||
|
||||
### `garbage_collection.add_date`
|
||||
|
||||
Add a date to the list of dates calculated automatically. To add multiple dates, call this service multiple-times with different dates.
|
||||
Note that this date will be removed on the next sensor update, when the data is re-calculated and loaded. This is why, this service should be called from the automation triggered by the event `garbage_collection_loaded`. This event is called each time the sensor is updated. And at the end of this automation, you need to call the `garbage_collection.update_state` service to update the sensor state based on automatically collected dates, and the dates added, removed, or offset by the automation.
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------- | :------------------------------------------------------------------------------------------- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
| `date` | The date to be added, in ISO format (`'yyyy-mm-dd'`). Make sure to enter the date in quotes! |
|
||||
|
||||
### `garbage_collection.remove_date`
|
||||
|
||||
Remove a date to the list of dates calculated automatically. To remove multiple dates, call this service multiple-times with different dates.
|
||||
Note that this date will be removed on the next sensor update, when the data is re-calculated and loaded. This is why, this service should be called from the automation triggered by the event `garbage_collection_loaded`. This event is called each time the sensor is updated. And at the end of this automation, you need to call the `garbage_collection.update_state` service to update the sensor state based on automatically collected dates, and the dates added, removed, or offset by the automation.
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------- | :--------------------------------------------------------------------------------------------- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
| `date` | The date to be removed, in ISO format (`'yyyy-mm-dd'`). Make sure to enter the date in quotes! |
|
||||
|
||||
### `garbage_collection.offset_date`
|
||||
|
||||
Offset the calculated collection day by the `offset` number of days.
|
||||
Note that this date will be removed on the next sensor update, when the data is re-calculated and loaded. This is why, this service should be called from the automation triggered by the event `garbage_collection_loaded`. This event is called each time the sensor is updated. And at the end of this automation, you need to call the `garbage_collection.update_state` service to update the sensor state based on automatically collected dates, and the dates added, removed, or offset by the automation.
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------- | :--------------------------------------------------------------------------------------------- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
| `date` | The date to be removed, in ISO format (`'yyyy-mm-dd'`). Make sure to enter the date in quotes! |
|
||||
| `offset` | By how many days to offset - integer between `-31` to `31` (e.g. `1`) |
|
||||
|
||||
### `garbage_collection.update_state`
|
||||
|
||||
Choose the next collection date from the list of dates calculated automatically, added by service calls (and not removed), and update the entity state and attributes.
|
||||
|
||||
| Attribute | Description |
|
||||
| :---------- | :------------------------------------------------------------- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
|
||||
## Events
|
||||
|
||||
### `garbage_collection_loaded`
|
||||
|
||||
This event is triggered each time a `garbage_collection` entity is being updated. You can create an automation to modify the collection schedule before the entity state update.
|
||||
|
||||
Event data:
|
||||
| Attribute | Description |
|
||||
| :-- | :-- |
|
||||
| `entity_id` | The garbage collection entity id (e.g. `sensor.general_waste`) |
|
||||
| `collection_dates` | List of collection dates calculated automatically. |
|
||||
|
||||
## Manual update examples
|
||||
|
||||
For the example below, the entity should be configured with `manual_update` set to `true`.
|
||||
Then, when the `garbage_collection` entity is updated (normally once a day at midnight, or restart, or when triggering entity update by script), it will calculate the collection schedule for previous, current and next year. But it will **NOT UPDATE** the entity state.
|
||||
Instead, it will trigger an event `garbage_collection_loaded` with a list of automatically calculated dates as a parameter.
|
||||
You will **have to create an automation triggered by this event**. In this automation, you will need to call the service `garbage_collection.update_state` to update the state. Before that, you can call the services `garbage_collection.add_date` and/or `garbage_collection.remove_date` and/or `garbage_collection.offset_date` to programmatically tweak the dates in whatever way you need (e.g. based on values from external API sensor, comparing the dates with the list of holidays, calculating custom offsets based on the day of the week etc.). This is complicated but gives you the ultimate flexibility.
|
||||
|
||||
## Simple example
|
||||
|
||||
Adding an extra collection date (a fixed date in this case) - for the entity `sensor.test`.
|
||||
|
||||
```yaml
|
||||
alias: garbage_collection event
|
||||
description: "Manually add a collection date, then trigger entity state update."
|
||||
trigger:
|
||||
- platform: event
|
||||
event_type: garbage_collection_loaded
|
||||
event_data:
|
||||
entity_id: sensor.test
|
||||
action:
|
||||
- service: garbage_collection.add_date
|
||||
data:
|
||||
entity_id: "{{ trigger.event.data.entity_id }}"
|
||||
date: "2022-01-07"
|
||||
- service: garbage_collection.update_state
|
||||
data:
|
||||
entity_id: sensor.test
|
||||
mode: single
|
||||
```
|
||||
|
||||
## Moderate example
|
||||
|
||||
This will loop through the calculated dates, and add an extra collection to a day after each calculated one. So if this is set for a collection every first Wednesday each month, it will result in a collection on the first Wednesday, and the following day (kind of first Thursday, except if the week is starting on Thursday - just a random weird example :).
|
||||
|
||||
This example is for an entity `sensor.test`. If you want to use it for yours, replace it with the real entity name in the trigger.
|
||||
|
||||
```yaml
|
||||
alias: test garbage_collection event
|
||||
description: "Loop through all calculated dates, add extra collection a day after the calculate one"
|
||||
trigger:
|
||||
- platform: event
|
||||
event_type: garbage_collection_loaded
|
||||
event_data:
|
||||
entity_id: sensor.test
|
||||
action:
|
||||
- repeat:
|
||||
for_each: "{{ trigger.event.data.collection_dates }}"
|
||||
sequence:
|
||||
- service: garbage_collection.add_date
|
||||
data:
|
||||
entity_id: "{{ trigger.event.data.entity_id }}"
|
||||
date: >-
|
||||
{{( as_datetime(repeat.item) + timedelta( days = 1)) | as_timestamp | timestamp_custom("%Y-%m-%d") }}
|
||||
- service: garbage_collection.update_state
|
||||
data:
|
||||
entity_id: "{{ trigger.event.data.entity_id }}"
|
||||
mode: single
|
||||
```
|
||||
|
||||
## Advanced example
|
||||
|
||||
This is an equivalent of "holiday in week" move - checking if there is a public holiday on the calculated collection day, or earlier in the week. And if yes, moving the collection by one day. This is fully custom logic, so it could be further complicated by whatever rules anyone wants.
|
||||
|
||||
This example is for an entity `sensor.test`. If you want to use it for yours, replace it with a real entity name in the trigger.
|
||||
|
||||
```yaml
|
||||
alias: test garbage_collection event
|
||||
description: >-
|
||||
Loop through all calculated dates, move the collection by 1 day if a public holiday was in the week before or on the calculated collection date calculate one
|
||||
trigger:
|
||||
- platform: event
|
||||
event_type: garbage_collection_loaded
|
||||
event_data:
|
||||
entity_id: sensor.test
|
||||
action:
|
||||
- repeat:
|
||||
for_each: "{{ trigger.event.data.collection_dates }}"
|
||||
sequence:
|
||||
- condition: template
|
||||
value_template: >-
|
||||
{%- set collection_date = as_datetime(repeat.item) %}
|
||||
{%- set ns = namespace(found=false) %}
|
||||
{%- for i in range(collection_date.weekday()+1) %}
|
||||
{%- set d = ( collection_date + timedelta( days=-i) ) | as_timestamp | timestamp_custom("%Y-%m-%d") %}
|
||||
{%- if d in state_attr(trigger.event.data.entity_id,'holidays') %}
|
||||
{%- set ns.found = true %}
|
||||
{%- endif %}
|
||||
{%- endfor %}
|
||||
{{ ns.found }}
|
||||
- service: garbage_collection.offset_date
|
||||
data:
|
||||
entity_id: "{{ trigger.event.data.entity_id }}"
|
||||
date: "{{ repeat.item }}"
|
||||
offset: 1
|
||||
- service: garbage_collection.update_state
|
||||
data:
|
||||
entity_id: "{{ trigger.event.data.entity_id }}"
|
||||
mode: single
|
||||
```
|
||||
|
||||
Or you can use the [blueprints](#blueprints-for-manual-update) I made for you. And you are welcome to create your own and share with others.
|
||||
|
||||
</details>
|
||||
|
||||
# Lovelace config examples
|
||||
|
||||
For information/inspiration - not supported.
|
||||
|
||||
<details>
|
||||
|
||||
## Garbage Collection custom card
|
||||
|
||||
You can use the custom [garbage collection card](https://github.com/amaximus/garbage-collection-card) developed by @amaximus.
|
||||
|
||||
<img src="https://github.com/amaximus/garbage-collection-card/blob/master/garbage_collection_lovelace.jpg">
|
||||
|
||||
## With images (picture-entity)
|
||||
|
||||
This is what I use (I like images). I use a horizontal stack of picture-entities, with `card-templater` plugin ([Lovelace Card Templater](https://github.com/gadgetchnnel/lovelace-card-templater)) to show the number of days:
|
||||
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/picture-entity.png">
|
||||
|
||||
(The `state` is designed to be used as traffic lights. That's why it has 3 values. You obviously cannot use this with `verbose_state`)
|
||||
|
||||
This is the configuration
|
||||
|
||||
```yaml
|
||||
- type: "custom:card-templater"
|
||||
card:
|
||||
type: picture-entity
|
||||
name_template: >-
|
||||
{{ state_attr('sensor.bio','days') }} days
|
||||
show_name: True
|
||||
show_state: False
|
||||
entity: sensor.bio
|
||||
state_image:
|
||||
"0": "/local/containers/bio_today.png"
|
||||
"1": "/local/containers/bio_tomorrow.png"
|
||||
"2": "/local/containers/bio_off.png"
|
||||
entities:
|
||||
- sensor.bio
|
||||
```
|
||||
|
||||
## List view (entities)
|
||||
|
||||
The simplest visualization is to use entities. In this case, I use `verbose_state` to show `state` as text.
|
||||
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/entities.png">
|
||||
|
||||
Lovelace configuration
|
||||
|
||||
```yaml
|
||||
- type: entities
|
||||
entities:
|
||||
- sensor.general_waste
|
||||
- sensor.bio
|
||||
- sensor.paper
|
||||
- sensor.plastic
|
||||
```
|
||||
|
||||
## Icon view (glance)
|
||||
|
||||
<img src="https://github.com/bruxy70/Garbage-Collection/blob/master/images/sensor.png">
|
||||
|
||||
Lovelace Configuration
|
||||
|
||||
```yaml
|
||||
- type: glance
|
||||
entities:
|
||||
- sensor.general_waste
|
||||
```
|
||||
|
||||
</details>
|
||||
Basé sur le projet original de bruxy70.
|
||||
Même licence que l’original.
|
||||
@@ -25,6 +25,10 @@ _LOGGER = logging.getLogger(__name__)
|
||||
months = [m["value"] for m in const.MONTH_OPTIONS]
|
||||
frequencies = [f["value"] for f in const.FREQUENCY_OPTIONS]
|
||||
|
||||
# -------------------------
|
||||
# SCHEMAS (inchangés)
|
||||
# -------------------------
|
||||
|
||||
SENSOR_SCHEMA = vol.Schema(
|
||||
{
|
||||
vol.Required(const.CONF_FREQUENCY): vol.In(frequencies),
|
||||
@@ -100,273 +104,83 @@ OFFSET_DATE_SCHEMA = vol.Schema(
|
||||
}
|
||||
)
|
||||
|
||||
# -------------------------
|
||||
# SETUP
|
||||
# -------------------------
|
||||
|
||||
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
|
||||
)
|
||||
|
||||
"""Set up platform - register services."""
|
||||
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."""
|
||||
# -------------------------
|
||||
# CONFIG ENTRY SETUP
|
||||
# -------------------------
|
||||
|
||||
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
|
||||
"""Set up via UI (Config Entry)."""
|
||||
|
||||
_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
|
||||
"Setting %s (%s)",
|
||||
entry.title,
|
||||
entry.options.get(const.CONF_FREQUENCY),
|
||||
)
|
||||
|
||||
entry.async_on_unload(entry.add_update_listener(update_listener))
|
||||
|
||||
await hass.config_entries.async_forward_entry_setups(
|
||||
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
|
||||
# -------------------------
|
||||
# REMOVE ENTRY
|
||||
# -------------------------
|
||||
|
||||
async def async_remove_entry(hass: HomeAssistant, entry: ConfigEntry) -> None:
|
||||
"""Handle removal."""
|
||||
await hass.config_entries.async_unload_platforms(
|
||||
entry,
|
||||
[const.SENSOR_PLATFORM],
|
||||
)
|
||||
|
||||
|
||||
# -------------------------
|
||||
# MIGRATION (inchangé)
|
||||
# -------------------------
|
||||
|
||||
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
|
||||
"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"
|
||||
)
|
||||
|
||||
# (tu peux garder ton code migration inchangé ici)
|
||||
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,
|
||||
)
|
||||
config_entry.data = MappingProxyType(new_data)
|
||||
config_entry.options = MappingProxyType(new_options)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
# -------------------------
|
||||
# UPDATE LISTENER FIXED
|
||||
# -------------------------
|
||||
|
||||
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)
|
||||
)
|
||||
"""Reload integration when options change."""
|
||||
|
||||
await hass.config_entries.async_reload(entry.entry_id)
|
||||
@@ -95,9 +95,9 @@ async def general_config_schema(
|
||||
const.DEFAULT_HOLIDAY_IN_WEEK_MOVE,
|
||||
): bool,
|
||||
optional(
|
||||
const.CONF_HOLIDAY_COUNTRY,
|
||||
const.CONF_HOLIDAY_DATES,
|
||||
handler.options,
|
||||
const.DEFAULT_HOLIDAY_COUNTRY,
|
||||
const.DEFAULT_HOLIDAY_DATES,
|
||||
): selector.TextSelector(),
|
||||
}
|
||||
)
|
||||
@@ -135,9 +135,9 @@ async def general_options_schema(
|
||||
const.DEFAULT_HOLIDAY_IN_WEEK_MOVE,
|
||||
): bool,
|
||||
optional(
|
||||
const.CONF_HOLIDAY_COUNTRY,
|
||||
const.CONF_HOLIDAY_DATES,
|
||||
handler.options,
|
||||
const.DEFAULT_HOLIDAY_COUNTRY,
|
||||
const.DEFAULT_HOLIDAY_DATES,
|
||||
): selector.TextSelector(),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -44,7 +44,7 @@ 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"
|
||||
CONF_HOLIDAY_DATES = "holiday_dates"
|
||||
|
||||
# Defaults
|
||||
DEFAULT_NAME = DOMAIN
|
||||
@@ -55,7 +55,7 @@ DEFAULT_PERIOD = 1
|
||||
DEFAULT_FIRST_WEEK = 1
|
||||
DEFAULT_VERBOSE_STATE = False
|
||||
DEFAULT_HOLIDAY_IN_WEEK_MOVE = False
|
||||
DEFAULT_HOLIDAY_COUNTRY = "FR"
|
||||
DEFAULT_HOLIDAY_DATES = "01-01,05-01,12-25"
|
||||
DEFAULT_DATE_FORMAT = "%d-%b-%Y"
|
||||
DEFAULT_VERBOSE_FORMAT = "on {date}, in {days} days"
|
||||
|
||||
|
||||
@@ -11,8 +11,7 @@
|
||||
"iot_class": "calculated",
|
||||
"issue_tracker": "https://github.com/bruxy70/Garbage-Collection/issues",
|
||||
"requirements": [
|
||||
"python-dateutil>=2.8.2",
|
||||
"holidays>=0.40"
|
||||
"python-dateutil>=2.8.2"
|
||||
],
|
||||
"version": "3.21.1"
|
||||
"version": "3.21.4"
|
||||
}
|
||||
@@ -5,8 +5,8 @@ import logging
|
||||
from datetime import date, datetime, time, timedelta
|
||||
from typing import Any, Dict, Generator
|
||||
|
||||
from dateutil.easter import easter
|
||||
from dateutil.relativedelta import relativedelta
|
||||
import holidays
|
||||
from homeassistant.config_entries import ConfigEntry
|
||||
from homeassistant.const import (
|
||||
ATTR_DEVICE_CLASS,
|
||||
@@ -71,8 +71,8 @@ class GarbageCollection(RestoreEntity):
|
||||
"_days",
|
||||
"_first_month",
|
||||
"_hidden",
|
||||
"_holiday_country",
|
||||
"_holidays_calendar",
|
||||
"_holiday_dates",
|
||||
"_move_holidays",
|
||||
"_icon_normal",
|
||||
"_icon_today",
|
||||
"_icon_tomorrow",
|
||||
@@ -126,23 +126,25 @@ class GarbageCollection(RestoreEntity):
|
||||
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
|
||||
self._holiday_dates: set[tuple[int, int]] = set()
|
||||
self._move_holidays = bool(config.get(const.CONF_MOVE_COUNTRY_HOLIDAYS, False))
|
||||
if self._move_holidays:
|
||||
raw_dates = config.get(
|
||||
const.CONF_HOLIDAY_DATES, const.DEFAULT_HOLIDAY_DATES
|
||||
)
|
||||
if config.get(const.CONF_MOVE_COUNTRY_HOLIDAYS, False):
|
||||
for chunk in raw_dates.split(","):
|
||||
chunk = chunk.strip()
|
||||
if not chunk:
|
||||
continue
|
||||
try:
|
||||
self._holidays_calendar = holidays.country_holidays(
|
||||
self._holiday_country
|
||||
)
|
||||
except NotImplementedError:
|
||||
month_str, day_str = chunk.split("-")
|
||||
self._holiday_dates.add((int(month_str), int(day_str)))
|
||||
except ValueError:
|
||||
_LOGGER.error(
|
||||
"(%s) Unknown country code for holidays: %s",
|
||||
"(%s) Invalid holiday date '%s', expected format MM-DD",
|
||||
self._attr_name,
|
||||
self._holiday_country,
|
||||
chunk,
|
||||
)
|
||||
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
|
||||
@@ -193,8 +195,8 @@ class GarbageCollection(RestoreEntity):
|
||||
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
|
||||
await self.hass.config_entries.async_forward_entry_setups(
|
||||
self.config_entry, [const.CALENDAR_PLATFORM]
|
||||
)
|
||||
|
||||
self.hass.data[const.DOMAIN][const.CALENDAR_PLATFORM].add_entity(
|
||||
@@ -378,27 +380,36 @@ class GarbageCollection(RestoreEntity):
|
||||
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.
|
||||
def _is_exception_date(self, day: date) -> bool:
|
||||
"""Check if a date matches a configured fixed exception, or a movable one.
|
||||
|
||||
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.
|
||||
Fixed exceptions come from the configured MM-DD list (e.g. Christmas,
|
||||
New Year, Labour Day for collectors that only shift on those).
|
||||
Movable exceptions are always checked too: Easter Monday and Whit
|
||||
Monday (Pentecost Monday) are the only French public holidays that
|
||||
ever fall on a Monday, which only matters for Monday-based schedules
|
||||
and is harmless to check for any other day.
|
||||
"""
|
||||
if self._holidays_calendar is None:
|
||||
if (day.month, day.day) in self._holiday_dates:
|
||||
return True
|
||||
easter_sunday = easter(day.year)
|
||||
easter_monday = easter_sunday + timedelta(days=1)
|
||||
pentecost_monday = easter_sunday + timedelta(days=50)
|
||||
return day in (easter_monday, pentecost_monday)
|
||||
|
||||
def _shift_for_holiday(self, collection_date: date) -> date:
|
||||
"""Move the date forward by one day for each matching exception date.
|
||||
|
||||
Cascades forward if the new day also matches an exception date.
|
||||
"""
|
||||
if not self._move_holidays:
|
||||
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 = collection_date
|
||||
while self._is_exception_date(shifted):
|
||||
shifted = shifted + timedelta(days=1)
|
||||
if shifted != collection_date:
|
||||
_LOGGER.debug(
|
||||
"(%s) %s shifted to %s because of a public holiday",
|
||||
"(%s) %s shifted to %s because of a configured exception date",
|
||||
self._attr_name,
|
||||
collection_date,
|
||||
shifted,
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
"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)"
|
||||
"holiday_dates": "Dates that shift collection by one day (format MM-DD, comma separated)"
|
||||
}
|
||||
},
|
||||
"detail": {
|
||||
@@ -69,7 +69,7 @@
|
||||
"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)"
|
||||
"holiday_dates": "Dates that shift collection by one day (format MM-DD, comma separated)"
|
||||
}
|
||||
},
|
||||
"detail": {
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
"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)"
|
||||
"holiday_dates": "Dates qui décalent la collecte d'un jour (format MM-JJ, séparées par des virgules)"
|
||||
}
|
||||
},
|
||||
"detail": {
|
||||
@@ -71,7 +71,7 @@
|
||||
"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)"
|
||||
"holiday_dates": "Dates qui décalent la collecte d'un jour (format MM-JJ, séparées par des virgules)"
|
||||
}
|
||||
},
|
||||
"detail": {
|
||||
|
||||
Reference in New Issue
Block a user