-
-
Save jgillula/787c5acfbd9a9a725a290dad8c4eae24 to your computer and use it in GitHub Desktop.
blueprint: | |
name: Advanced Appliance Power Monitor | |
description: | | |
Monitors the activity of an appliance based on the power it uses (W) after given delays, and triggers actions based on start, running (power usage changed), and stop. You can use the variables | |
* `{{ elapsed_time }}` (total seconds) | |
* `{{ elapsed_time_days }}` | |
* `{{ elapsed_time_hours }}` | |
* `{{ elapsed_time_minutes }}` | |
* `{{ elapsed_time_seconds }}` | |
* `{{ energy_used }}` | |
* `{{ event_type }}` (start, running, or stop) | |
* `{{ start_threshold }}` | |
* `{{ start_time }}` (a unix timestamp) | |
* `{{ finish_threshold }}` | |
in all the steps (start, running, and stop). | |
domain: automation | |
input: | |
power_entity: | |
name: Power Entity | |
description: Entity representing the power consumption of the appliance (W). | |
selector: | |
entity: | |
domain: sensor | |
device_class: power | |
energy_entity: | |
name: Energy Entity | |
description: (*Optional*) Entity representing the energy consumption of the appliance. | |
selector: | |
entity: | |
domain: sensor | |
device_class: energy | |
default: null | |
start_threshold: | |
name: Starting Threshold | |
description: Power threshold above which we assume the appliance has started. | |
default: 1 | |
selector: | |
number: | |
min: 0 | |
max: 100000 | |
unit_of_measurement: "W" | |
mode: box | |
finish_threshold: | |
name: Finishing Threshold | |
description: Power threshold below which we assume the appliance has finished. | |
default: 1 | |
selector: | |
number: | |
min: 0 | |
max: 100000 | |
unit_of_measurement: "W" | |
mode: box | |
start_threshold_delay: | |
name: Starting Threshold Delay | |
description: (*Optional*) How long the appliance power must be above Starting Threshold before triggering the Start Action. | |
default: | |
hours: 0 | |
minutes: 0 | |
seconds: 0 | |
selector: | |
duration: | |
running_threshold_delay: | |
name: Running Threshold Delay | |
description: | | |
(*Optional*) How long the appliance power must have changed before triggering the Running Action. | |
***You must set this larger than Finishing Threshold Delay below. If you don't it will automatically be set 1 second greater than Finishing Delay for you.*** | |
default: | |
hours: 0 | |
minutes: 0 | |
seconds: 0 | |
selector: | |
duration: | |
finish_threshold_delay: | |
name: Finishing Threshold Delay | |
description: (*Optional*) How long the appliance power must be below Finishing Threshold before triggering the Stop Action. | |
default: | |
hours: 0 | |
minutes: 0 | |
seconds: 0 | |
selector: | |
duration: | |
on_start: | |
name: Start Action | |
description: (*Optional*) Actions to perform when the appliance starts. | |
default: [] | |
selector: | |
action: {} | |
on_running: | |
name: Running Action | |
description: (*Optional*) Actions to perform when the appliance is running. | |
default: [] | |
selector: | |
action: {} | |
on_stop: | |
name: Stop Action | |
description: (*Optional*) Actions to perform when the appliance stops. | |
default: [] | |
selector: | |
action: {} | |
trigger: | |
- platform: numeric_state | |
entity_id: !input "power_entity" | |
for: !input "start_threshold_delay" | |
above: !input "start_threshold" | |
action: | |
- variables: | |
start_threshold: !input "start_threshold" | |
finish_threshold: !input "finish_threshold" | |
start_threshold_delay: !input "start_threshold_delay" | |
running_threshold_delay: !input "running_threshold_delay" | |
finish_threshold_delay: !input "finish_threshold_delay" | |
start_time: "{{ as_timestamp(now()) - start_threshold_delay.hours * 3600 - start_threshold_delay.minutes * 60 - start_threshold_delay.seconds }}" | |
energy_entity: !input "energy_entity" | |
initial_energy: > | |
{% if energy_entity is not none %} | |
{{ states(energy_entity) | float }} | |
{% else %} | |
0 | |
{% endif %} | |
- variables: | |
elapsed_time: > | |
{% set elapsed_timedelta = now() - as_datetime(start_time) %} {{ | |
elapsed_timedelta.days * 86400 + elapsed_timedelta.seconds }} | |
elapsed_days: "{{ (elapsed_time | int) // 86400 }}" | |
elapsed_hours: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * 86400)) // | |
3600 }} | |
elapsed_minutes: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * 86400) - | |
((elapsed_hours|int) * 3600)) // 60 }} | |
elapsed_seconds: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * 86400) - | |
((elapsed_hours|int) * 3600) - ((elapsed_minutes|int) * 60)) }} | |
event_type: start | |
energy_used: > | |
{% if energy_entity is not none %} | |
{{ (states(energy_entity) | float) - initial_energy }} | |
{% else %} | |
0 | |
{% endif %} | |
# We do this next trick to make sure running_threshold_delay is greater than finish_threshold_delay, otherwise we'll never detect the finish event because the running event will always be triggered first | |
running_threshold_total_delay: "{{ running_threshold_delay.hours * 3600 + running_threshold_delay.minutes * 60 + running_threshold_delay.seconds }}" | |
finish_threshold_total_delay: "{{ finish_threshold_delay.hours * 3600 + finish_threshold_delay.minutes * 60 + finish_threshold_delay.seconds }}" | |
- variables: | |
running_threshold_total_delay: "{{ (finish_threshold_total_delay + 1) if (running_threshold_total_delay < finish_threshold_total_delay) else running_threshold_total_delay }}" | |
running_threshold_delay_hours: "{{ running_threshold_total_delay // 3600 }}" | |
running_threshold_delay_minutes: "{{ (running_threshold_total_delay - running_threshold_delay_hours * 3600) // 60 }}" | |
running_threshold_delay_seconds: "{{ (running_threshold_total_delay - running_threshold_delay_hours * 3600 - running_threshold_delay_minutes * 60) }}" | |
running_threshold_delay: | |
hours: "{{ running_threshold_delay_hours }}" | |
minutes: "{{ running_threshold_delay_minutes }}" | |
seconds: "{{ running_threshold_delay_seconds }}" | |
- choose: | |
- conditions: "{{ true }}" | |
sequence: !input "on_start" | |
- repeat: | |
until: | |
- condition: template | |
value_template: '{{ wait.trigger.id == "stop" }}' | |
sequence: | |
- wait_for_trigger: | |
- platform: numeric_state | |
entity_id: !input "power_entity" | |
for: "{{ finish_threshold_delay.hours }}:{{ finish_threshold_delay.minutes }}:{{ finish_threshold_delay.seconds }}" | |
below: !input "finish_threshold" | |
id: stop | |
- platform: state | |
entity_id: | |
- !input "power_entity" | |
id: running | |
for: "{{ running_threshold_delay.hours }}:{{ running_threshold_delay.minutes }}:{{ running_threshold_delay.seconds }}" | |
- variables: | |
event_type: "{{ wait.trigger.id }}" | |
energy_used: > | |
{% if energy_entity is not none %} | |
{{ (states(energy_entity) | float) - initial_energy }} | |
{% else %} | |
0 | |
{% endif %} | |
- choose: | |
- conditions: "{{ event_type == 'running' }}" | |
sequence: | |
- variables: | |
elapsed_time: "{% set elapsed_timedelta = now() - as_datetime(start_time) %}{{ elapsed_timedelta.days * 86400 + elapsed_timedelta.seconds }}" | |
elapsed_days: "{{ (elapsed_time | int) // 86400 }}" | |
elapsed_hours: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400)) // 3600 }} | |
elapsed_minutes: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400) - ((elapsed_hours|int) * 3600)) // 60 }} | |
elapsed_seconds: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400) - ((elapsed_hours|int) * 3600) - | |
((elapsed_minutes|int) * 60)) }} | |
- choose: | |
conditions: "{{ true }}" | |
sequence: !input "on_running" | |
- conditions: "{{ event_type == 'stop' }}" | |
sequence: | |
- variables: | |
elapsed_time: > | |
{% set elapsed_timedelta = now() - as_datetime(start_time | |
+ finish_threshold_delay.hours * 3600 + finish_threshold_delay.minutes * | |
60 + finish_threshold_delay.seconds) %} {{ elapsed_timedelta.days | |
* 86400 + elapsed_timedelta.seconds }} | |
elapsed_days: "{{ (elapsed_time | int) // 86400 }}" | |
elapsed_hours: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400)) // 3600 }} | |
elapsed_minutes: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400) - ((elapsed_hours|int) * 3600)) // 60 }} | |
elapsed_seconds: >- | |
{{ ((elapsed_time | int) - ((elapsed_days|int) * | |
86400) - ((elapsed_hours|int) * 3600) - | |
((elapsed_minutes|int) * 60)) }} | |
- choose: | |
- conditions: "{{ true }}" | |
sequence: !input "on_stop" | |
mode: single |
I've tried for many hours to include the elapsed_time when the appliance stops in a notification, but failed. An example would be of great use to me.
I've tried for many hours to include the elapsed_time when the appliance stops in a notification, but failed. An example would be of great use to me.
Here's what I use, hope it helps:
{% set elapsed_time_string = (((24 * (elapsed_days|int) + (elapsed_hours|int)) | string) + " hours, " if ((elapsed_hours|int) > 0 or (elapsed_days|int) > 0) else "") + (elapsed_minutes|string) + " minutes" %}
The dryer ran for {{ elapsed_time_string }}.
If the number of days or hours is greater than zero, it combines them into a total number of hours, and then adds the minutes.
With my code:
- id: '1697054838140'
alias: sumpAPM
description: ''
use_blueprint:
path: jgillula/advanced_appliance_power_monitor.yaml
input:
power_entity: sensor.sump_pump_plug_current_consumption
energy_entity: sensor.sump_pump_plug_today_s_consumption
on_stop:
- service: notify.mobile_app_bucks_iphone
{% set elapsed_time_string = (((24 * (elapsed_days|int) + (elapsed_hours|int)) | string) + " hours, " if ((elapsed_hours|int) > 0 or (elapsed_days|int) > 0) else "") + (elapsed_minutes|string) + " minutes" %}
data:
message: The pump ran {{ elapsed_time_string }} seconds.
I get the error msg "UndefinedError: 'elapsed_hours' is undefined". I thought the path: statement would have enabled this reference.
Does it work if you move the Jinja template inside the message
field, i.e.
message: >-
{% set elapsed_time_string = (((24 * (elapsed_days|int) + (elapsed_hours|int)) | string) + " hours, " if ((elapsed_hours|int) > 0 or (elapsed_days|int) > 0) else "") + (elapsed_minutes|string) + " minutes" %}
The pump ran {{ elapsed_time_string }} seconds.
(Also note that elapsed_time_string
is going to be something like "27 hours, 34 minutes", not a number of seconds.)
This updated code:
- id: "1697054838140"
alias: sumpAPM
description: ""
use_blueprint:
path: jgillula/advanced_appliance_power_monitor.yaml
input:
power_entity: sensor.sump_pump_plug_current_consumption
energy_entity: sensor.sump_pump_plug_today_s_consumption
on_stop:
- service: notify.mobile_app_bucks_iphone
data:
message: >-
{% set elapsed_time_string = (((24 * (elapsed_days|int) + (elapsed_hours|int)) | string) + " hours, " if ((elapsed_hours|int) > 0 or (elapsed_days|int) > 0) else "") + (elapsed_minutes|string) + " minutes" %}
The pump ran {{ elapsed_time_string }}
throws the same error "UndefinedError: 'elapsed_hours' is undefined" (I'm testing in Developer Tools->Template)
How would you use this to display the elapsed time? I am unsure about the best approach. should I have a separate automation that updates every second another helper and then display it in lovelace dashboard, or is there a better way to do so?
You could use it to display the elapsed time after it's complete, but this automation really isn't designed to display the elapsed time in a Lovelace dashboard as the appliance is still running. I think it'd be easier to take the relevant bits from this automation and write your own new one.
Based heavily on the original at https://gist.github.com/Jhonattan-Souza/619df98d8766f0cd7987734c70fe19f0