Skip to content

Commit

Permalink
Merge pull request #316 from bruxy70/development
Browse files Browse the repository at this point in the history
Release 4.2
  • Loading branch information
bruxy70 authored Jan 16, 2022
2 parents 340795f + aaf7db5 commit e780366
Show file tree
Hide file tree
Showing 8 changed files with 348 additions and 28 deletions.
15 changes: 10 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@

# Garbage Collection

The `garbage_collection` component is a Home Assistant custom sensor for monitoring regular garbage collection schedule. The sensor can be configured for number of different schedules:
The `garbage_collection` componnent is a Home Assistant integration that creates a custom sensor for monitoring regular garbage collection schedule. The sensor can be configured for number of different schedules:
- `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 - it therefore restarts each year, as the week numbers start again from 1.
- 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 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 - in case you want 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.

Expand Down Expand Up @@ -115,7 +116,7 @@ Entity_id change is not possible using the YAML configuration. Changing other pa
|Attribute |Required|Description
|:----------|----------|------------
| `name` | Yes | Sensor friendly name
| `frequency` | Yes | `"weekly"`, `"even-weeks"`, `"odd-weeks"`, `"every-n-weeks"`, `"every-n-days"`, `"monthly"`, `"annual"` or `"group"`
| `frequency` | Yes | `"weekly"`, `"even-weeks"`, `"odd-weeks"`, `"every-n-weeks"`, `"every-n-days"`, `"monthly"`, `"annual"`, `"group"` or `"blank"`
| `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-example).</br>**Default**: `False`
| `offset` | No | Offset calculated date by `offset` days (makes most sense for monthly frequency). Examples of use:</br>for last Saurday each month, configure first Saturday each month with `offset: -7`</br>for 1<sup>st</sup> Wednesday in of full week, configure first Monday each month with `offset: 2`</br>(integer between -31 and 31) **Default**: 0
| `hidden` | No | Hide in calendar (useful for sensors that are used in groups)<br/>**Default**: `False`
Expand All @@ -128,7 +129,7 @@ Entity_id change is not possible using the YAML configuration. Changing other pa
| `date_format` | No | In the `verbose_format`, you can configure the format of date (using [strftime](http://strftime.org/) format) **Default**: `'%d-%b-%Y'`


#### PARAMETERS FOR ALL FREQUENCIES EXCEPT ANNUAL AND GROUP
#### PARAMETERS FOR ALL FREQUENCIES EXCEPT ANNUAL, GROUP and BLANK
|Attribute |Required|Description
|:----------|----------|------------
| `first_month` | No | Month three letter abbreviation, e.g. `"jan"`, `"feb"`...<br/>**Default**: `"jan"`
Expand All @@ -144,7 +145,7 @@ Entity_id change is not possible using the YAML configuration. Changing other pa
| `observed` | No | Country holidays - observed (see [holidays](https://github.com/dr-prodigy/python-holidays) ).


#### PARAMETERS FOR ALL FREQUENCIES EXCEPT ANNUAL, EVERY-N-DAYS and GROUP
#### PARAMETERS FOR ALL FREQUENCIES EXCEPT ANNUAL, EVERY-N-DAYS, GROUP and BLANK
|Attribute |Required|Description
|:----------|----------|------------
| `collection_days` | Yes | Day three letter abbreviation, list of `"mon"`, `"tue"`, `"wed"`, `"thu"`, `"fri"`, `"sat"`, `"sun"`.
Expand Down Expand Up @@ -240,6 +241,8 @@ Then, when the `garbage_collection` entity is updated (normally once a day at mi
Instead, it will trigger an event `garbage_collection_loaded` with 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 servics `garbage_collection.add_date` and/or `garbage_collection.remove_date` to programatically tweak the dates in whatever way you need (e.g. based on values from external API sensor, comparing the dates with list of holidays, calculating custom offsets based on the day of the week etc.). This is complicated, but gives you an ultimate flexibility.

I made couple of [Blueprints](https://github.com/bruxy70/Garbage-Collection/tree/development/blueprints) to automatically move collection on pubic holidays using the manual update. The goal is to simplify this integration moving forward by removing the holidays feature and moving that to bluprints called by manual update. I am also in a process of creating a dedicated integration just for holidays. So you can check it out now and let me know. If you try that, just make sure you either use holidays from another `garbage_collection` entity, or set the `move_offset` to `0` - otherwise both the integration and the blueprint will move the collection, so it might move twice I think.

### 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 data is re-calculated and loaded. This is why this service should be called from the automation triggered be the event `garbage_collection_loaded`, that 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 with the dates added, removed or offset by the automation.
Expand Down Expand Up @@ -357,7 +360,7 @@ action:
sequence:
- condition: template
value_template: >-
{%- set collection_date = as_datetime(trigger.event.data.collection_dates[repeat.index]) %}
{%- set collection_date = as_datetime(trigger.event.data.collection_dates[repeat.index-1]) %}
{%- 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") %}
Expand All @@ -377,6 +380,8 @@ action:
mode: single
```

Or you can use the [blueprints](https://github.com/bruxy70/Garbage-Collection/tree/development/blueprints) I made for you. And you are welcome to create your own and share with the others.

# Lovelace config examples

## Garbage Collection custom card
Expand Down
63 changes: 63 additions & 0 deletions blueprints/holiday_in_week.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
blueprint:
name: Holiday in week
description: Loop through all calculated dates, move the collection to the next day
if public holiday was in the week before/on the calculated collection date.
source_url: https://github.com/bruxy70
domain: automation
input:
garbage_collection_entity:
name: Garbage Collection Entity
description: Triggered by the event for this entity.
selector:
entity:
integration: garbage_collection
holiday_entity:
name: Holidays (a garbage collection integration entity)
description: Entity containing the holidays. If you use the same
entity for the calendar and the collection schedule, make sure to configure
the collection schedule integration offset to 0, otherwise it will be moved twice.
selector:
entity:
integration: garbage_collection
mode: parallel
trigger:
- platform: event
event_type: garbage_collection_loaded
event_data:
entity_id: !input garbage_collection_entity
action:
- variables:
holiday_entity: !input holiday_entity
- repeat:
count: '{{ trigger.event.data.collection_dates | count }}'
sequence:
- condition: template
value_template: >-
{%- set collection_date = as_datetime(trigger.event.data.collection_dates[repeat.index-1]) %}
{%- 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(holiday_entity,'holidays') %}
{%- set ns.found = true %}
{%- endif %}
{%- endfor %}
{{ ns.found }}
- service: garbage_collection.offset_date
data:
entity_id: "{{ trigger.event.data.entity_id }}"
date: '{{ trigger.event.data.collection_dates[repeat.index] }}'
offset: >-
{%- set collection_date = as_datetime(trigger.event.data.collection_dates[repeat.index-1]) %}
{%- set ns = namespace(offset=1, found=false) %}
{%- for _ in range(7) if not ns.found %}
{%- set d = ( collection_date + timedelta( days=ns.offset) ) | as_timestamp | timestamp_custom("%Y-%m-%d") %}
{%- if d in state_attr(holiday_entity,'holidays') %}
{%- set ns.offset = ns.offset + 1 %}
{% else %}
{%- set ns.found = true %}
{%- endif %}
{% endfor %}
{{ ns.offset }}
- service: garbage_collection.update_state
data:
entity_id: "{{ trigger.event.data.entity_id }}"
53 changes: 53 additions & 0 deletions blueprints/holiday_in_week_simple.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
blueprint:
name: Holiday in week (simple)
description: Loop through all calculated dates, move the collection to the next day
if public holiday was in the week before/on the calculated collection date.
It will always move the collection by 1 day, even if the new day was also a public holiday.
source_url: https://github.com/bruxy70
domain: automation
input:
garbage_collection_entity:
name: Garbage Collection Entity
description: Triggered by the event for this entity.
selector:
entity:
integration: garbage_collection
holiday_entity:
name: Holidays (a garbage collection integration entity)
description: Entity containing the holidays. If you use the same
entity for the calendar and the collection schedule, make sure to configure
the collection schedule integration offset to 0, otherwise it will be moved twice.
selector:
entity:
integration: garbage_collection
mode: parallel
trigger:
- platform: event
event_type: garbage_collection_loaded
event_data:
entity_id: !input garbage_collection_entity
action:
- variables:
holiday_entity: !input holiday_entity
- repeat:
count: '{{ trigger.event.data.collection_dates | count }}'
sequence:
- condition: template
value_template: >-
{%- set collection_date = as_datetime(trigger.event.data.collection_dates[repeat.index-1]) %}
{%- 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(holiday_entity,'holidays') %}
{%- set ns.found = true %}
{%- endif %}
{%- endfor %}
{{ ns.found }}
- service: garbage_collection.offset_date
data:
entity_id: "{{ trigger.event.data.entity_id }}"
date: '{{ trigger.event.data.collection_dates[repeat.index] }}'
offset: 1
- service: garbage_collection.update_state
data:
entity_id: "{{ trigger.event.data.entity_id }}"
7 changes: 3 additions & 4 deletions custom_components/garbage_collection/config_flow.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
CONF_INCLUDE_DATES,
CONF_WEEK_ORDER_NUMBER,
CONF_WEEKDAY_ORDER_NUMBER,
DAILY_FREQUENCY,
DAILY_BLANK_FREQUENCY,
DOMAIN,
GROUP_FREQUENCY,
MONTHLY_FREQUENCY,
Expand Down Expand Up @@ -113,7 +113,6 @@ def step1_user_init(self, user_input: Dict, defaults=None):
# Do not show name for Options_Flow. The name cannot be changed here
if defaults is not None and CONF_NAME in self.data_schema:
del self.data_schema[CONF_NAME]

return False

def step2_annual_group(self, user_input: Dict, defaults=None):
Expand Down Expand Up @@ -286,7 +285,7 @@ async def async_step_user(
if next_step:
if self.shared_class.frequency in ANNUAL_GROUP_FREQUENCY:
return await self.async_step_annual_group()
elif self.shared_class.frequency in DAILY_FREQUENCY:
elif self.shared_class.frequency in DAILY_BLANK_FREQUENCY:
return await self.async_step_final()
else:
return await self.async_step_detail()
Expand Down Expand Up @@ -389,7 +388,7 @@ async def async_step_init(self, user_input=None):
if next_step:
if self.shared_class.frequency in ANNUAL_GROUP_FREQUENCY:
return await self.async_step_annual_group()
elif self.shared_class.frequency in DAILY_FREQUENCY:
elif self.shared_class.frequency in DAILY_BLANK_FREQUENCY:
return await self.async_step_final()
else:
return await self.async_step_detail()
Expand Down
22 changes: 17 additions & 5 deletions custom_components/garbage_collection/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"every-n-days",
"monthly",
"annual",
"blank",
"group",
]

Expand All @@ -102,14 +103,25 @@
"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"]

MONTH_OPTIONS = [
"jan",
Expand Down Expand Up @@ -332,7 +344,7 @@ class configuration(config_singularity):
},
CONF_COLLECTION_DAYS: {
"step": 3,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP_BLANK,
"method": vol.Optional,
"type": [str],
"validator": vol.All(cv.ensure_list, [vol.In(WEEKDAYS)]),
Expand All @@ -357,14 +369,14 @@ class configuration(config_singularity):
},
CONF_FIRST_MONTH: {
"step": 4,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP_BLANK,
"method": vol.Optional,
"default": DEFAULT_FIRST_MONTH,
"type": vol.In(MONTH_OPTIONS),
},
CONF_LAST_MONTH: {
"step": 4,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP_BLANK,
"method": vol.Optional,
"default": DEFAULT_LAST_MONTH,
"type": vol.In(MONTH_OPTIONS),
Expand Down Expand Up @@ -400,7 +412,7 @@ class configuration(config_singularity):
},
CONF_HOLIDAY_MOVE_OFFSET: {
"step": 4,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP_BLANK,
"default": 1,
"method": vol.Optional,
"type": int,
Expand All @@ -415,7 +427,7 @@ class configuration(config_singularity):
},
CONF_HOLIDAY_IN_WEEK_MOVE: {
"step": 4,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP,
"valid_for": lambda f: f in EXCEPT_ANNUAL_GROUP_BLANK,
"method": vol.Optional,
"default": DEFAULT_HOLIDAY_IN_WEEK_MOVE,
"type": bool,
Expand Down
Loading

0 comments on commit e780366

Please sign in to comment.