Update appliance monitoring configuration
This commit is contained in:
Binary file not shown.
|
After Width: | Height: | Size: 7.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
@@ -0,0 +1 @@
|
|||||||
|
https://zorruno:5HwEar6Gu@home.fox.co.nz
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
{"pid": 71, "version": 1, "ha_version": "2026.6.1", "start_ts": 1780991968.2470338}
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,763 @@
|
|||||||
|
################################################################################
|
||||||
|
# PACKAGE: Appliance Monitor Processor
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Version: 1.2
|
||||||
|
# Date: 2026-06-09
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Shared appliance power monitoring processor.
|
||||||
|
#
|
||||||
|
# This script is called by appliances_status_monitoring.yaml. It reads the
|
||||||
|
# previous appliance monitor state from the appliance monitor MQTT sensor,
|
||||||
|
# calculates the next state and cycle totals, publishes retained JSON state
|
||||||
|
# back to MQTT, and calls the View Road announcement router when a cycle
|
||||||
|
# finishes.
|
||||||
|
#
|
||||||
|
# Design:
|
||||||
|
# - One shared processing script.
|
||||||
|
# - MQTT retained JSON state per appliance.
|
||||||
|
# - One MQTT sensor per appliance.
|
||||||
|
# - Timestamp-based energy and cost calculation.
|
||||||
|
# - Rolling average state detection.
|
||||||
|
#
|
||||||
|
# Default values:
|
||||||
|
# debug_flow: false
|
||||||
|
# resolution: 7
|
||||||
|
# standby_power: 2
|
||||||
|
# operating_power_min: 12
|
||||||
|
# paid_tariff_start: "00:00"
|
||||||
|
# paid_tariff_end: "21:00"
|
||||||
|
# paid_tariff_cost: 0.2023
|
||||||
|
# offpeak_tariff_cost: 0.0
|
||||||
|
# max_sample_gap_seconds: 900
|
||||||
|
# min_cycle_notify_seconds: 300
|
||||||
|
#
|
||||||
|
# States:
|
||||||
|
# Off
|
||||||
|
# Standby
|
||||||
|
# Operating
|
||||||
|
# Finished
|
||||||
|
#
|
||||||
|
# Notes:
|
||||||
|
# Version 1.2:
|
||||||
|
# - Added min_cycle_notify_seconds.
|
||||||
|
# - Finished cycles shorter than this are still recorded, but do not send
|
||||||
|
# notifications.
|
||||||
|
# - Added MQTT attributes showing whether notification was allowed or
|
||||||
|
# suppressed.
|
||||||
|
#
|
||||||
|
# Version 1.1:
|
||||||
|
# - Accepts lower-case retained states from previous package versions, such
|
||||||
|
# as standby, operating, and finished.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
# FINISHED ANNOUNCEMENT
|
||||||
|
# The finishing script calls the package view_road_announcement_router.yaml.
|
||||||
|
# If you want to use your own announcement system, it will occur at the end
|
||||||
|
# at am_cycle_notify_allowed being true, so change to your own announcement
|
||||||
|
# method there.
|
||||||
|
#
|
||||||
|
# Announcements can use:
|
||||||
|
# am_announcement_channels
|
||||||
|
# am_announcement_title
|
||||||
|
# am_short_announcement
|
||||||
|
# am_long_announcement
|
||||||
|
# am_matrix_indicator
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
script:
|
||||||
|
appliance_monitor_process_sample:
|
||||||
|
alias: Appliance Monitor - Process Sample
|
||||||
|
icon: mdi:meter-electric
|
||||||
|
mode: queued
|
||||||
|
max: 50
|
||||||
|
|
||||||
|
fields:
|
||||||
|
appliance_id:
|
||||||
|
name: Appliance ID
|
||||||
|
description: Short unique ID, for example washing_machine.
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
||||||
|
appliance_name:
|
||||||
|
name: Appliance Name
|
||||||
|
description: Human readable appliance name.
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
||||||
|
appliance_action:
|
||||||
|
name: Appliance Action
|
||||||
|
description: Human readable action, for example washing.
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
||||||
|
power_w:
|
||||||
|
name: Power W
|
||||||
|
description: Current instantaneous appliance power in watts.
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 10000
|
||||||
|
step: 0.1
|
||||||
|
unit_of_measurement: W
|
||||||
|
|
||||||
|
monitor_sensor:
|
||||||
|
name: Monitor Sensor
|
||||||
|
description: MQTT monitor sensor used to read previous state.
|
||||||
|
selector:
|
||||||
|
entity:
|
||||||
|
domain: sensor
|
||||||
|
|
||||||
|
state_topic:
|
||||||
|
name: MQTT State Topic
|
||||||
|
description: Retained MQTT topic used to publish monitor state JSON.
|
||||||
|
selector:
|
||||||
|
text:
|
||||||
|
|
||||||
|
min_cycle_notify_seconds:
|
||||||
|
name: Minimum Cycle Notify Seconds
|
||||||
|
description: Finished cycles shorter than this are recorded but do not send notifications.
|
||||||
|
default: 300
|
||||||
|
selector:
|
||||||
|
number:
|
||||||
|
min: 0
|
||||||
|
max: 86400
|
||||||
|
step: 1
|
||||||
|
unit_of_measurement: s
|
||||||
|
|
||||||
|
sequence:
|
||||||
|
##########################################################################
|
||||||
|
# CORE DEFAULTS AND PASSED VALUES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_appliance_id: "{{ appliance_id | default('appliance', true) | string }}"
|
||||||
|
am_appliance_name: "{{ appliance_name | default('Appliance', true) | string }}"
|
||||||
|
am_appliance_action: "{{ appliance_action | default('running', true) | string }}"
|
||||||
|
am_power_w: "{{ power_w | default(0, true) | float(0) }}"
|
||||||
|
am_monitor_sensor: "{{ monitor_sensor | default('', true) | string }}"
|
||||||
|
am_state_topic: "{{ state_topic | default('', true) | string }}"
|
||||||
|
|
||||||
|
am_debug_flow: "{{ debug_flow | default(false, true) | bool(false) }}"
|
||||||
|
am_resolution: "{{ resolution | default(7, true) | int(7) }}"
|
||||||
|
am_standby_power: "{{ standby_power | default(2, true) | float(2) }}"
|
||||||
|
am_operating_power_min: "{{ operating_power_min | default(12, true) | float(12) }}"
|
||||||
|
|
||||||
|
am_paid_tariff_start: "{{ paid_tariff_start | default('00:00', true) | string }}"
|
||||||
|
am_paid_tariff_end: "{{ paid_tariff_end | default('21:00', true) | string }}"
|
||||||
|
am_paid_tariff_cost: "{{ paid_tariff_cost | default(0.2023, true) | float(0.2023) }}"
|
||||||
|
am_offpeak_tariff_cost: "{{ offpeak_tariff_cost | default(0.0, true) | float(0.0) }}"
|
||||||
|
|
||||||
|
am_max_sample_gap_seconds: "{{ max_sample_gap_seconds | default(900, true) | int(900) }}"
|
||||||
|
am_min_cycle_notify_seconds: "{{ min_cycle_notify_seconds | default(300, true) | int(300) }}"
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# ANNOUNCEMENT DEFAULTS AND PASSED VALUES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_announcement_channels: >-
|
||||||
|
{{ announcement_channels | default('pushover_zorruno, sony_tv_lounge, lounge_google_home_voice, led_matrix_1', true) | string }}
|
||||||
|
|
||||||
|
am_short_announcement: >-
|
||||||
|
{{ short_announcement | default((am_appliance_action | title) ~ ' complete', true) | string }}
|
||||||
|
|
||||||
|
am_long_announcement_override: >-
|
||||||
|
{{ long_announcement | default('', true) | string }}
|
||||||
|
|
||||||
|
am_announcement_title: >-
|
||||||
|
{{ title | default(am_appliance_name ~ ' Notification', true) | string }}
|
||||||
|
|
||||||
|
am_matrix_indicator: "{{ matrix_indicator | default('', true) | string }}"
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# PREVIOUS STATE FROM MQTT MONITOR SENSOR ATTRIBUTES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_now_ts: "{{ as_timestamp(now()) }}"
|
||||||
|
|
||||||
|
am_previous_state: >-
|
||||||
|
{%- set s = states(am_monitor_sensor) | string | lower | trim -%}
|
||||||
|
{{ {'operating': 'Operating', 'finished': 'Finished', 'standby': 'Standby', 'off': 'Off'}.get(s, 'Off') }}
|
||||||
|
|
||||||
|
am_last_power_w: "{{ state_attr(am_monitor_sensor, 'last_power_w') | float(am_power_w) }}"
|
||||||
|
am_last_sample_ts: "{{ state_attr(am_monitor_sensor, 'last_sample_ts') | float(0) }}"
|
||||||
|
|
||||||
|
am_existing_cycle_start_ts: "{{ state_attr(am_monitor_sensor, 'cycle_start_ts') | float(0) }}"
|
||||||
|
am_existing_cycle_stop_ts: "{{ state_attr(am_monitor_sensor, 'cycle_stop_ts') | float(0) }}"
|
||||||
|
|
||||||
|
am_existing_cycle_energy_kwh: "{{ state_attr(am_monitor_sensor, 'cycle_energy_kwh') | float(0) }}"
|
||||||
|
am_existing_cycle_paid_energy_kwh: "{{ state_attr(am_monitor_sensor, 'cycle_paid_energy_kwh') | float(0) }}"
|
||||||
|
am_existing_cycle_free_energy_kwh: "{{ state_attr(am_monitor_sensor, 'cycle_free_energy_kwh') | float(0) }}"
|
||||||
|
am_existing_cycle_cost: "{{ state_attr(am_monitor_sensor, 'cycle_cost') | float(0) }}"
|
||||||
|
|
||||||
|
am_previous_recent_samples_json: >-
|
||||||
|
{% set raw = state_attr(am_monitor_sensor, 'recent_samples_json') | string | trim %}
|
||||||
|
{{ raw if raw.startswith('[') else '[]' }}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# ELAPSED TIME
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_elapsed_sec_raw: >-
|
||||||
|
{% if am_last_sample_ts | float(0) > 0 %}
|
||||||
|
{{ (am_now_ts | float(0)) - (am_last_sample_ts | float(0)) }}
|
||||||
|
{% else %}
|
||||||
|
0
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_effective_elapsed_sec: >-
|
||||||
|
{% set elapsed = am_elapsed_sec_raw | float(0) %}
|
||||||
|
{% if elapsed <= 0 %}
|
||||||
|
0
|
||||||
|
{% elif elapsed > (am_max_sample_gap_seconds | int(900)) %}
|
||||||
|
{{ am_max_sample_gap_seconds | int(900) }}
|
||||||
|
{% else %}
|
||||||
|
{{ elapsed }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# ROLLING AVERAGE
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_previous_recent_samples_list: >-
|
||||||
|
{% set raw_json = state_attr(am_monitor_sensor, 'recent_samples_json') %}
|
||||||
|
{% set raw_list = state_attr(am_monitor_sensor, 'recent_samples') %}
|
||||||
|
{% if raw_json is string %}
|
||||||
|
{{ raw_json | from_json(default=[]) }}
|
||||||
|
{% elif raw_json is iterable and raw_json is not string %}
|
||||||
|
{{ raw_json | list }}
|
||||||
|
{% elif raw_list is iterable and raw_list is not string %}
|
||||||
|
{{ raw_list | list }}
|
||||||
|
{% else %}
|
||||||
|
[]
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_recent_samples_list: >-
|
||||||
|
{% set old = am_previous_recent_samples_list %}
|
||||||
|
{% set ns = namespace(items=[am_power_w | float(0)]) %}
|
||||||
|
{% for item in old %}
|
||||||
|
{% if loop.index < (am_resolution | int(7)) %}
|
||||||
|
{% set ns.items = ns.items + [item | float(0)] %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{{ ns.items }}
|
||||||
|
|
||||||
|
am_recent_samples_json: >-
|
||||||
|
{{ am_recent_samples_list | to_json }}
|
||||||
|
|
||||||
|
am_average_power_w: >-
|
||||||
|
{% set samples = am_recent_samples_list %}
|
||||||
|
{% if samples | count > 0 %}
|
||||||
|
{{ ((samples | sum) / (samples | count)) | round(3) }}
|
||||||
|
{% else %}
|
||||||
|
0
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# STATE MACHINE
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_new_state: >-
|
||||||
|
{% set avg = am_average_power_w | float(0) %}
|
||||||
|
{% set standby = am_standby_power | float(2) %}
|
||||||
|
{% set operate = am_operating_power_min | float(12) %}
|
||||||
|
{% set prev = am_previous_state | string %}
|
||||||
|
{% if prev == 'Operating' and avg < operate %}
|
||||||
|
Finished
|
||||||
|
{% elif avg < standby %}
|
||||||
|
Off
|
||||||
|
{% elif avg >= operate %}
|
||||||
|
Operating
|
||||||
|
{% elif avg >= standby and prev in ['Off', 'Finished'] %}
|
||||||
|
Standby
|
||||||
|
{% else %}
|
||||||
|
{{ prev }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_state_changed: "{{ (am_new_state | string) != (am_previous_state | string) }}"
|
||||||
|
am_cycle_started: "{{ am_new_state == 'Operating' and am_previous_state != 'Operating' }}"
|
||||||
|
am_cycle_finished: "{{ am_new_state == 'Finished' and am_previous_state == 'Operating' }}"
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# ACCUMULATION WINDOW
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_accumulation_seconds: >-
|
||||||
|
{% set elapsed = am_effective_elapsed_sec | float(0) %}
|
||||||
|
{% if elapsed <= 0 %}
|
||||||
|
0
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ elapsed }}
|
||||||
|
{% elif am_previous_state != 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ elapsed / 2 }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Finished' %}
|
||||||
|
{{ elapsed / 2 }}
|
||||||
|
{% else %}
|
||||||
|
0
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_accumulation_average_power_w: >-
|
||||||
|
{% set last = am_last_power_w | float(0) %}
|
||||||
|
{% set current = am_power_w | float(0) %}
|
||||||
|
{% set operate = am_operating_power_min | float(12) %}
|
||||||
|
{% if am_previous_state == 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ ((last + current) / 2) | round(3) }}
|
||||||
|
{% elif am_previous_state != 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ ((operate + current) / 2) | round(3) }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Finished' %}
|
||||||
|
{{ ((last + operate) / 2) | round(3) }}
|
||||||
|
{% else %}
|
||||||
|
0
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_accumulation_start_ts: >-
|
||||||
|
{% set elapsed = am_effective_elapsed_sec | float(0) %}
|
||||||
|
{% set accum = am_accumulation_seconds | float(0) %}
|
||||||
|
{% if accum <= 0 %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ (am_now_ts | float(0)) - elapsed }}
|
||||||
|
{% elif am_previous_state != 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ (am_now_ts | float(0)) - accum }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Finished' %}
|
||||||
|
{{ (am_now_ts | float(0)) - elapsed }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_accumulation_stop_ts: >-
|
||||||
|
{% set elapsed = am_effective_elapsed_sec | float(0) %}
|
||||||
|
{% set accum = am_accumulation_seconds | float(0) %}
|
||||||
|
{% if accum <= 0 %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% elif am_previous_state != 'Operating' and am_new_state == 'Operating' %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% elif am_previous_state == 'Operating' and am_new_state == 'Finished' %}
|
||||||
|
{{ (am_now_ts | float(0)) - elapsed + accum }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_interval_mid_ts: >-
|
||||||
|
{% set start = am_accumulation_start_ts | float(0) %}
|
||||||
|
{% set accum = am_accumulation_seconds | float(0) %}
|
||||||
|
{% if accum > 0 %}
|
||||||
|
{{ start + (accum / 2) }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# TARIFF CALCULATION
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_paid_start_minutes: >-
|
||||||
|
{% set p = (am_paid_tariff_start | string).split(':') %}
|
||||||
|
{% set hh = p[0] if p | count > 0 else 0 %}
|
||||||
|
{% set mm = p[1] if p | count > 1 else 0 %}
|
||||||
|
{{ (hh | int(0)) * 60 + (mm | int(0)) }}
|
||||||
|
|
||||||
|
am_paid_end_minutes: >-
|
||||||
|
{% set p = (am_paid_tariff_end | string).split(':') %}
|
||||||
|
{% set hh = p[0] if p | count > 0 else 0 %}
|
||||||
|
{% set mm = p[1] if p | count > 1 else 0 %}
|
||||||
|
{{ (hh | int(0)) * 60 + (mm | int(0)) }}
|
||||||
|
|
||||||
|
am_interval_mid_minutes: >-
|
||||||
|
{% set h = am_interval_mid_ts | float(0) | timestamp_custom('%H', true) | int(0) %}
|
||||||
|
{% set m = am_interval_mid_ts | float(0) | timestamp_custom('%M', true) | int(0) %}
|
||||||
|
{{ h * 60 + m }}
|
||||||
|
|
||||||
|
am_is_paid_interval: >-
|
||||||
|
{% set start = am_paid_start_minutes | int(0) %}
|
||||||
|
{% set end = am_paid_end_minutes | int(0) %}
|
||||||
|
{% set mid = am_interval_mid_minutes | int(0) %}
|
||||||
|
{% if start == end %}
|
||||||
|
true
|
||||||
|
{% elif start < end %}
|
||||||
|
{{ mid >= start and mid < end }}
|
||||||
|
{% else %}
|
||||||
|
{{ mid >= start or mid < end }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_tariff_rate: >-
|
||||||
|
{% if am_is_paid_interval | bool(false) %}
|
||||||
|
{{ am_paid_tariff_cost | float(0.2023) }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_offpeak_tariff_cost | float(0.0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# INTERVAL ENERGY AND COST
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_interval_energy_kwh: >-
|
||||||
|
{{
|
||||||
|
(
|
||||||
|
(am_accumulation_average_power_w | float(0))
|
||||||
|
* (am_accumulation_seconds | float(0))
|
||||||
|
/ 3600000
|
||||||
|
) | round(6)
|
||||||
|
}}
|
||||||
|
|
||||||
|
am_interval_cost: >-
|
||||||
|
{{ ((am_interval_energy_kwh | float(0)) * (am_tariff_rate | float(0))) | round(6) }}
|
||||||
|
|
||||||
|
am_should_accumulate: "{{ (am_accumulation_seconds | float(0)) > 0 }}"
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# CYCLE TOTAL BASES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_cycle_energy_base_kwh: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
0
|
||||||
|
{% else %}
|
||||||
|
{{ am_existing_cycle_energy_kwh | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_paid_energy_base_kwh: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
0
|
||||||
|
{% else %}
|
||||||
|
{{ am_existing_cycle_paid_energy_kwh | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_free_energy_base_kwh: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
0
|
||||||
|
{% else %}
|
||||||
|
{{ am_existing_cycle_free_energy_kwh | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_cost_base: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
0
|
||||||
|
{% else %}
|
||||||
|
{{ am_existing_cycle_cost | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# CYCLE TOTALS
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_cycle_energy_new_kwh: >-
|
||||||
|
{% set add_kwh = am_interval_energy_kwh | float(0) if am_should_accumulate | bool(false) else 0 %}
|
||||||
|
{{ ((am_cycle_energy_base_kwh | float(0)) + add_kwh) | round(6) }}
|
||||||
|
|
||||||
|
am_cycle_cost_new: >-
|
||||||
|
{% set add_cost = am_interval_cost | float(0) if am_should_accumulate | bool(false) else 0 %}
|
||||||
|
{{ ((am_cycle_cost_base | float(0)) + add_cost) | round(6) }}
|
||||||
|
|
||||||
|
am_cycle_paid_energy_new_kwh: >-
|
||||||
|
{% set add_kwh = am_interval_energy_kwh | float(0) if (am_should_accumulate | bool(false) and am_is_paid_interval | bool(false)) else 0 %}
|
||||||
|
{{ ((am_cycle_paid_energy_base_kwh | float(0)) + add_kwh) | round(6) }}
|
||||||
|
|
||||||
|
am_cycle_free_energy_new_kwh: >-
|
||||||
|
{% set add_kwh = am_interval_energy_kwh | float(0) if (am_should_accumulate | bool(false) and not (am_is_paid_interval | bool(false))) else 0 %}
|
||||||
|
{{ ((am_cycle_free_energy_base_kwh | float(0)) + add_kwh) | round(6) }}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# CYCLE TIMES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_cycle_start_ts_new: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
{{ am_accumulation_start_ts | float(0) }}
|
||||||
|
{% elif am_existing_cycle_start_ts | float(0) > 0 %}
|
||||||
|
{{ am_existing_cycle_start_ts | float(0) }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_stop_ts_new: >-
|
||||||
|
{% if am_cycle_finished | bool(false) %}
|
||||||
|
{{ am_accumulation_stop_ts | float(0) }}
|
||||||
|
{% elif am_cycle_started | bool(false) %}
|
||||||
|
{{ am_accumulation_start_ts | float(0) }}
|
||||||
|
{% elif am_existing_cycle_stop_ts | float(0) > 0 %}
|
||||||
|
{{ am_existing_cycle_stop_ts | float(0) }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_now_ts | float(0) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_duration_seconds_new: >-
|
||||||
|
{% set start = am_cycle_start_ts_new | float(0) %}
|
||||||
|
{% if am_new_state == 'Operating' %}
|
||||||
|
{% set stop = am_now_ts | float(0) %}
|
||||||
|
{% else %}
|
||||||
|
{% set stop = am_cycle_stop_ts_new | float(0) %}
|
||||||
|
{% endif %}
|
||||||
|
{% if start > 0 and stop > start %}
|
||||||
|
{{ (stop - start) | int(0) }}
|
||||||
|
{% else %}
|
||||||
|
0
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# FORMATTED VALUES
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_cycle_energy_formatted: >-
|
||||||
|
{% set kwh = am_cycle_energy_new_kwh | float(0) %}
|
||||||
|
{% if kwh >= 1 %}
|
||||||
|
{{ '%.1f' | format(kwh) }}kWh
|
||||||
|
{% elif kwh < 0.001 %}
|
||||||
|
<1Wh
|
||||||
|
{% else %}
|
||||||
|
{{ '%.1f' | format(kwh * 1000) }}Wh
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_cost_formatted: >-
|
||||||
|
{% set val = am_cycle_cost_new | float(0) %}
|
||||||
|
{% if val < 0.01 %}
|
||||||
|
<1c
|
||||||
|
{% elif val < 1 %}
|
||||||
|
{{ (val * 100) | round(0) | int(0) }}c
|
||||||
|
{% else %}
|
||||||
|
${{ '%.2f' | format(val) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_offpeak_savings: >-
|
||||||
|
{% set val = ((am_cycle_energy_new_kwh | float(0)) * (am_paid_tariff_cost | float(0.2023))) - (am_cycle_cost_new | float(0)) %}
|
||||||
|
{{ ([0, val] | max) | round(4) }}
|
||||||
|
|
||||||
|
am_cycle_offpeak_savings_formatted: >-
|
||||||
|
{% set val = am_cycle_offpeak_savings | float(0) %}
|
||||||
|
{% if val < 0.01 %}
|
||||||
|
<1c
|
||||||
|
{% elif val < 1 %}
|
||||||
|
{{ (val * 100) | round(0) | int(0) }}c
|
||||||
|
{% else %}
|
||||||
|
${{ '%.2f' | format(val) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_duration_formatted: >-
|
||||||
|
{% set d = am_cycle_duration_seconds_new | int(0) %}
|
||||||
|
{% set h = (d // 3600) | int(0) %}
|
||||||
|
{% set m = ((d % 3600) // 60) | int(0) %}
|
||||||
|
{% if h < 1 %}
|
||||||
|
{{ '%02dmin' | format(m) }}
|
||||||
|
{% else %}
|
||||||
|
{{ '%02dh %02dmin' | format(h, m) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_cycle_start_time_formatted: >-
|
||||||
|
{{ am_cycle_start_ts_new | float(0) | timestamp_custom('%H:%M', true) }}
|
||||||
|
|
||||||
|
am_cycle_stop_time_formatted: >-
|
||||||
|
{{ am_cycle_stop_ts_new | float(0) | timestamp_custom('%H:%M', true) }}
|
||||||
|
|
||||||
|
am_cycle_start_local: >-
|
||||||
|
{{ am_cycle_start_ts_new | float(0) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }}
|
||||||
|
|
||||||
|
am_cycle_stop_local: >-
|
||||||
|
{{ am_cycle_stop_ts_new | float(0) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }}
|
||||||
|
|
||||||
|
am_last_sample_local: >-
|
||||||
|
{{ am_now_ts | float(0) | timestamp_custom('%Y-%m-%d %H:%M:%S', true) }}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# SUMMARY STRINGS
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_summary_short: >-
|
||||||
|
Your {{ am_appliance_action }} is complete.
|
||||||
|
|
||||||
|
am_summary: >-
|
||||||
|
Your {{ am_appliance_action }} is complete.
|
||||||
|
It started at {{ am_cycle_start_time_formatted | trim }},
|
||||||
|
finished at {{ am_cycle_stop_time_formatted | trim }},
|
||||||
|
and used {{ am_cycle_energy_formatted | trim }},
|
||||||
|
taking {{ am_cycle_duration_formatted | trim }}
|
||||||
|
at a cost of {{ am_cycle_cost_formatted | trim }}
|
||||||
|
(Off-peak savings of {{ am_cycle_offpeak_savings_formatted | trim }}).
|
||||||
|
|
||||||
|
am_last_summary_new: >-
|
||||||
|
{% if am_cycle_started | bool(false) %}
|
||||||
|
Cycle running
|
||||||
|
{% elif am_cycle_finished | bool(false) %}
|
||||||
|
{{ am_summary }}
|
||||||
|
{% else %}
|
||||||
|
{{ state_attr(am_monitor_sensor, 'last_summary') | default('No cycle recorded', true) }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
am_long_announcement: >-
|
||||||
|
{% if am_long_announcement_override | trim | length > 0 %}
|
||||||
|
{{ am_long_announcement_override }}
|
||||||
|
{% else %}
|
||||||
|
{{ am_summary }}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# NOTIFICATION DECISION
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# Very short cycles are still recorded, but no announcement is sent.
|
||||||
|
# Default minimum is 300 seconds / 5 minutes.
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- variables:
|
||||||
|
am_cycle_notify_allowed: >-
|
||||||
|
{{
|
||||||
|
am_cycle_finished | bool(false)
|
||||||
|
and (am_cycle_duration_seconds_new | int(0)) >= (am_min_cycle_notify_seconds | int(300))
|
||||||
|
}}
|
||||||
|
|
||||||
|
am_cycle_notification_suppressed: >-
|
||||||
|
{{
|
||||||
|
am_cycle_finished | bool(false)
|
||||||
|
and not (am_cycle_notify_allowed | bool(false))
|
||||||
|
}}
|
||||||
|
|
||||||
|
am_cycle_notification_suppressed_reason: >-
|
||||||
|
{% if am_cycle_notification_suppressed | bool(false) %}
|
||||||
|
Cycle was shorter than {{ am_min_cycle_notify_seconds | int(300) }} seconds
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# PUBLISH RETAINED MQTT STATE
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- action: mqtt.publish
|
||||||
|
data:
|
||||||
|
topic: "{{ am_state_topic }}"
|
||||||
|
retain: true
|
||||||
|
qos: 0
|
||||||
|
payload: >-
|
||||||
|
{{
|
||||||
|
dict(
|
||||||
|
appliance_id=am_appliance_id,
|
||||||
|
appliance_name=am_appliance_name,
|
||||||
|
appliance_action=am_appliance_action,
|
||||||
|
|
||||||
|
state=am_new_state,
|
||||||
|
previous_state=am_previous_state,
|
||||||
|
state_changed=am_state_changed | bool(false),
|
||||||
|
|
||||||
|
power_w=am_power_w | float(0),
|
||||||
|
last_power_w=am_power_w | float(0),
|
||||||
|
previous_power_w=am_last_power_w | float(0),
|
||||||
|
average_power_w=am_average_power_w | float(0),
|
||||||
|
recent_samples=am_recent_samples_list,
|
||||||
|
recent_samples_json=am_recent_samples_json,
|
||||||
|
|
||||||
|
last_sample_ts=am_now_ts | float(0),
|
||||||
|
last_sample_local=am_last_sample_local,
|
||||||
|
|
||||||
|
cycle_start_ts=am_cycle_start_ts_new | float(0),
|
||||||
|
cycle_stop_ts=am_cycle_stop_ts_new | float(0),
|
||||||
|
cycle_start_local=am_cycle_start_local,
|
||||||
|
cycle_stop_local=am_cycle_stop_local,
|
||||||
|
|
||||||
|
cycle_duration_seconds=am_cycle_duration_seconds_new | int(0),
|
||||||
|
cycle_duration_formatted=am_cycle_duration_formatted | trim,
|
||||||
|
|
||||||
|
min_cycle_notify_seconds=am_min_cycle_notify_seconds | int(300),
|
||||||
|
cycle_notify_allowed=am_cycle_notify_allowed | bool(false),
|
||||||
|
cycle_notification_suppressed=am_cycle_notification_suppressed | bool(false),
|
||||||
|
cycle_notification_suppressed_reason=am_cycle_notification_suppressed_reason | trim,
|
||||||
|
|
||||||
|
cycle_energy_kwh=am_cycle_energy_new_kwh | float(0),
|
||||||
|
cycle_paid_energy_kwh=am_cycle_paid_energy_new_kwh | float(0),
|
||||||
|
cycle_free_energy_kwh=am_cycle_free_energy_new_kwh | float(0),
|
||||||
|
cycle_energy_formatted=am_cycle_energy_formatted | trim,
|
||||||
|
|
||||||
|
cycle_cost=am_cycle_cost_new | float(0),
|
||||||
|
cycle_cost_formatted=am_cycle_cost_formatted | trim,
|
||||||
|
|
||||||
|
cycle_offpeak_savings=am_cycle_offpeak_savings | float(0),
|
||||||
|
cycle_offpeak_savings_formatted=am_cycle_offpeak_savings_formatted | trim,
|
||||||
|
|
||||||
|
paid_tariff_start=am_paid_tariff_start,
|
||||||
|
paid_tariff_end=am_paid_tariff_end,
|
||||||
|
paid_tariff_cost=am_paid_tariff_cost | float(0),
|
||||||
|
offpeak_tariff_cost=am_offpeak_tariff_cost | float(0),
|
||||||
|
|
||||||
|
accumulation_seconds=am_accumulation_seconds | float(0),
|
||||||
|
accumulation_average_power_w=am_accumulation_average_power_w | float(0),
|
||||||
|
interval_energy_kwh=am_interval_energy_kwh | float(0),
|
||||||
|
interval_cost=am_interval_cost | float(0),
|
||||||
|
is_paid_interval=am_is_paid_interval | bool(false),
|
||||||
|
|
||||||
|
summary_short=am_summary_short,
|
||||||
|
summary=am_summary,
|
||||||
|
last_summary=am_last_summary_new
|
||||||
|
) | to_json
|
||||||
|
}}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# DEBUG LOGBOOK ENTRY
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ am_debug_flow | bool(false) }}"
|
||||||
|
then:
|
||||||
|
- action: logbook.log
|
||||||
|
data:
|
||||||
|
name: "Appliance Monitor"
|
||||||
|
message: >-
|
||||||
|
{{ am_appliance_name }}: {{ am_previous_state }} -> {{ am_new_state }},
|
||||||
|
power {{ am_power_w | round(1) }}W,
|
||||||
|
average {{ am_average_power_w | round(1) }}W,
|
||||||
|
cycle {{ am_cycle_energy_new_kwh | round(3) }}kWh,
|
||||||
|
cost {{ am_cycle_cost_new | round(4) }}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# SHORT CYCLE SUPPRESSION LOG
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ am_cycle_notification_suppressed | bool(false) }}"
|
||||||
|
then:
|
||||||
|
- action: logbook.log
|
||||||
|
data:
|
||||||
|
name: "Appliance Monitor"
|
||||||
|
message: >-
|
||||||
|
{{ am_appliance_name }} finished after
|
||||||
|
{{ am_cycle_duration_formatted | trim }}, below the
|
||||||
|
{{ am_min_cycle_notify_seconds | int(300) }} second notification
|
||||||
|
threshold. No announcement was sent.
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# FINISHED ANNOUNCEMENT
|
||||||
|
##########################################################################
|
||||||
|
# This script is in additional package view_road_announcement_router.yaml.
|
||||||
|
# If you want to use your own announcement system, it will occur here
|
||||||
|
# at am_cycle_notify_allowed being true.
|
||||||
|
#
|
||||||
|
# Change to your own announcement method here if needed.
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- if:
|
||||||
|
- condition: template
|
||||||
|
value_template: "{{ am_cycle_notify_allowed | bool(false) }}"
|
||||||
|
then:
|
||||||
|
- action: script.view_road_announcement
|
||||||
|
data:
|
||||||
|
channels: "{{ am_announcement_channels }}"
|
||||||
|
title: "{{ am_announcement_title }}"
|
||||||
|
short_announcement: "{{ am_short_announcement }}"
|
||||||
|
long_announcement: "{{ am_long_announcement }}"
|
||||||
|
payload: ""
|
||||||
|
indicator: "{{ am_matrix_indicator }}"
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
################################################################################
|
||||||
|
# PACKAGE: Appliances Status Monitoring
|
||||||
|
################################################################################
|
||||||
|
#
|
||||||
|
# Version: 1.4
|
||||||
|
# Date: 2026-06-09
|
||||||
|
#
|
||||||
|
# Purpose:
|
||||||
|
# Appliance status monitoring package for multiple appliances.
|
||||||
|
#
|
||||||
|
# This package defines a central appliance registry and one automation that:
|
||||||
|
#
|
||||||
|
# 1. Publishes MQTT discovery config for each appliance monitor sensor.
|
||||||
|
# 2. Samples appliance power sensors once per minute.
|
||||||
|
# 3. Calls script.appliance_monitor_process_sample.
|
||||||
|
#
|
||||||
|
# Processor package required:
|
||||||
|
# appliance_monitor_processor.yaml
|
||||||
|
#
|
||||||
|
# Announcement router required:
|
||||||
|
# view_road_announcement_router.yaml
|
||||||
|
#
|
||||||
|
# MQTT:
|
||||||
|
# MQTT is used only as retained state storage for appliance monitor sensors.
|
||||||
|
# Sensor entities are created using MQTT discovery.
|
||||||
|
#
|
||||||
|
# Adding another appliance:
|
||||||
|
# Add one entry to the am_appliances list.
|
||||||
|
#
|
||||||
|
# Version 1.4:
|
||||||
|
# Added min_cycle_notify_seconds support.
|
||||||
|
# Default is 300 seconds, so cycles shorter than 5 minutes are recorded but
|
||||||
|
# do not send finished notifications.
|
||||||
|
# Per-appliance overrides can be set in the am_appliances list.
|
||||||
|
#
|
||||||
|
# Version 1.3:
|
||||||
|
# Removed the is_number gate before calling the processor. The processor is
|
||||||
|
# now always called once per appliance per sample, and power is safely converted
|
||||||
|
# to 0 if the source sensor is unavailable or non-numeric.
|
||||||
|
#
|
||||||
|
################################################################################
|
||||||
|
|
||||||
|
automation:
|
||||||
|
- id: appliances_status_monitoring_process_samples
|
||||||
|
alias: Appliances Status Monitoring - Process Samples
|
||||||
|
description: Publish MQTT discovery, sample appliance power sensors, and process appliance status/cycle data.
|
||||||
|
mode: queued
|
||||||
|
max: 20
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
- platform: homeassistant
|
||||||
|
event: start
|
||||||
|
id: ha_start
|
||||||
|
|
||||||
|
- platform: time_pattern
|
||||||
|
minutes: "/1"
|
||||||
|
id: periodic_sample
|
||||||
|
|
||||||
|
variables:
|
||||||
|
##########################################################################
|
||||||
|
# MQTT LOCAL CONTROLS
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
mqtt_device_name: "appliance_monitor"
|
||||||
|
mqtt_local_status_topic: !secret mqtt_status_main_topic
|
||||||
|
mqtt_discovery_prefix: "homeassistant"
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# DEFAULTS
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
am_default_debug_flow: false
|
||||||
|
am_default_resolution: 2
|
||||||
|
am_default_standby_power: 2
|
||||||
|
am_default_operating_power_min: 12
|
||||||
|
am_default_min_cycle_notify_seconds: 300
|
||||||
|
|
||||||
|
#am_default_announcement_channels: "pushover_zorruno, sony_tv_lounge, lounge_google_home_voice, led_matrix_1"
|
||||||
|
am_default_announcement_channels: "pushover_zorruno"
|
||||||
|
|
||||||
|
# Optional appliance-specific overrides available:
|
||||||
|
# resolution: 4
|
||||||
|
# The number of samples for the average, with one sample per minute.
|
||||||
|
#
|
||||||
|
# standby_power: 3
|
||||||
|
# Below this value, the appliance will show "Off".
|
||||||
|
# Between this value and operating_power_min, it will show "Standby".
|
||||||
|
#
|
||||||
|
# operating_power_min: 20
|
||||||
|
# The minimum average power needed for the appliance to show "Operating".
|
||||||
|
#
|
||||||
|
# min_cycle_notify_seconds: 600
|
||||||
|
# Finished cycles shorter than this are recorded, but no notification
|
||||||
|
# is sent. Default is 300 seconds.
|
||||||
|
#
|
||||||
|
# announcement_channels: "pushover_zorruno"
|
||||||
|
# Announcement channels for this appliance.
|
||||||
|
#
|
||||||
|
# long_announcement: "The device has finished. Please empty it when convenient."
|
||||||
|
# Override the long announcement.
|
||||||
|
#
|
||||||
|
# min_cycle_notify_seconds: 600
|
||||||
|
# Minimum time for an appliance to be operating before a completion announcement generated.
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# APPLIANCE REGISTRY
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
am_appliances:
|
||||||
|
- appliance_id: washing_machine
|
||||||
|
appliance_name: "Washing Machine"
|
||||||
|
appliance_action: "washing2"
|
||||||
|
power_entity: "sensor.esp_laundrywashpow_load_power"
|
||||||
|
icon: "mdi:washing-machine"
|
||||||
|
short_announcement: "/s/sWashing complete"
|
||||||
|
matrix_indicator: "wash,1,15,10,6,5,1,0"
|
||||||
|
# Optional washing-machine-specific overrides
|
||||||
|
resolution: 5
|
||||||
|
standby_power: 3
|
||||||
|
operating_power_min: 20
|
||||||
|
announcement_channels: "pushover_zorruno"
|
||||||
|
|
||||||
|
- appliance_id: dryer
|
||||||
|
appliance_name: "Dryer"
|
||||||
|
appliance_action: "drying2"
|
||||||
|
power_entity: "sensor.esp_laundrydrypow_load_power"
|
||||||
|
icon: "mdi:tumble-dryer"
|
||||||
|
short_announcement: "/s/sDryer complete"
|
||||||
|
matrix_indicator: "dry,1,15,10,6,5,1,0"
|
||||||
|
# Optional dryer-specific overrides
|
||||||
|
resolution: 4
|
||||||
|
standby_power: 3
|
||||||
|
operating_power_min: 20
|
||||||
|
announcement_channels: "pushover_zorruno"
|
||||||
|
|
||||||
|
- appliance_id: main_dishwasher
|
||||||
|
appliance_name: "Main Dishwasher"
|
||||||
|
appliance_action: "dishwashing2"
|
||||||
|
power_entity: "sensor.esp_maindishwasherpower_power"
|
||||||
|
icon: "mdi:dishwasher"
|
||||||
|
short_announcement: "/sDishwasher complete"
|
||||||
|
matrix_indicator: "dish,1,15,10,6,5,1,0"
|
||||||
|
|
||||||
|
action:
|
||||||
|
##########################################################################
|
||||||
|
# PUBLISH / REFRESH MQTT DISCOVERY CONFIG
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- repeat:
|
||||||
|
for_each: "{{ am_appliances }}"
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
am_state_topic: >-
|
||||||
|
{{ mqtt_local_status_topic }}/{{ mqtt_device_name }}/{{ repeat.item.appliance_id }}/state
|
||||||
|
|
||||||
|
am_discovery_topic: >-
|
||||||
|
{{ mqtt_discovery_prefix }}/sensor/{{ mqtt_device_name }}_{{ repeat.item.appliance_id }}/config
|
||||||
|
|
||||||
|
am_unique_id: >-
|
||||||
|
{{ repeat.item.appliance_id }}_monitor
|
||||||
|
|
||||||
|
am_object_id: >-
|
||||||
|
{{ mqtt_device_name }}_{{ repeat.item.appliance_id }}_monitor
|
||||||
|
|
||||||
|
- action: mqtt.publish
|
||||||
|
data:
|
||||||
|
topic: "{{ am_discovery_topic }}"
|
||||||
|
retain: true
|
||||||
|
qos: 0
|
||||||
|
payload: >-
|
||||||
|
{{
|
||||||
|
dict(
|
||||||
|
name=repeat.item.appliance_name ~ ' Monitor',
|
||||||
|
unique_id=am_unique_id,
|
||||||
|
object_id=am_object_id,
|
||||||
|
icon=repeat.item.icon | default('mdi:power-plug'),
|
||||||
|
state_topic=am_state_topic,
|
||||||
|
value_template='{{ value_json.state | default("Off") }}',
|
||||||
|
json_attributes_topic=am_state_topic,
|
||||||
|
device=dict(
|
||||||
|
identifiers=[mqtt_device_name],
|
||||||
|
name='Appliance Monitor',
|
||||||
|
manufacturer='View Road Home Assistant',
|
||||||
|
model='Shared appliance power monitor'
|
||||||
|
)
|
||||||
|
) | to_json
|
||||||
|
}}
|
||||||
|
|
||||||
|
##########################################################################
|
||||||
|
# PROCESS SAMPLES
|
||||||
|
##########################################################################
|
||||||
|
#
|
||||||
|
# Always process every appliance. The processor receives power_w as a safe
|
||||||
|
# float value, so unavailable/non-numeric sensors become 0 W.
|
||||||
|
#
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
- repeat:
|
||||||
|
for_each: "{{ am_appliances }}"
|
||||||
|
sequence:
|
||||||
|
- variables:
|
||||||
|
am_state_topic: >-
|
||||||
|
{{ mqtt_local_status_topic }}/{{ mqtt_device_name }}/{{ repeat.item.appliance_id }}/state
|
||||||
|
|
||||||
|
am_monitor_sensor: >-
|
||||||
|
sensor.{{ mqtt_device_name }}_{{ repeat.item.appliance_id }}_monitor
|
||||||
|
|
||||||
|
am_title: >-
|
||||||
|
{{ repeat.item.title | default(repeat.item.appliance_name ~ ' Notification', true) }}
|
||||||
|
|
||||||
|
am_announcement_channels: >-
|
||||||
|
{{ repeat.item.announcement_channels | default(am_default_announcement_channels, true) }}
|
||||||
|
|
||||||
|
am_debug_flow: >-
|
||||||
|
{{ repeat.item.debug_flow | default(am_default_debug_flow, true) }}
|
||||||
|
|
||||||
|
am_resolution: >-
|
||||||
|
{{ repeat.item.resolution | default(am_default_resolution, true) }}
|
||||||
|
|
||||||
|
am_standby_power: >-
|
||||||
|
{{ repeat.item.standby_power | default(am_default_standby_power, true) }}
|
||||||
|
|
||||||
|
am_operating_power_min: >-
|
||||||
|
{{ repeat.item.operating_power_min | default(am_default_operating_power_min, true) }}
|
||||||
|
|
||||||
|
am_min_cycle_notify_seconds: >-
|
||||||
|
{{ repeat.item.min_cycle_notify_seconds | default(am_default_min_cycle_notify_seconds, true) }}
|
||||||
|
|
||||||
|
am_power_w: >-
|
||||||
|
{{ states(repeat.item.power_entity) | float(0) }}
|
||||||
|
|
||||||
|
am_long_announcement: >-
|
||||||
|
{{ repeat.item.long_announcement | default('', true) }}
|
||||||
|
|
||||||
|
- action: script.appliance_monitor_process_sample
|
||||||
|
data:
|
||||||
|
appliance_id: "{{ repeat.item.appliance_id }}"
|
||||||
|
appliance_name: "{{ repeat.item.appliance_name }}"
|
||||||
|
appliance_action: "{{ repeat.item.appliance_action }}"
|
||||||
|
|
||||||
|
power_w: "{{ am_power_w }}"
|
||||||
|
monitor_sensor: "{{ am_monitor_sensor }}"
|
||||||
|
state_topic: "{{ am_state_topic }}"
|
||||||
|
|
||||||
|
debug_flow: "{{ am_debug_flow }}"
|
||||||
|
resolution: "{{ am_resolution }}"
|
||||||
|
standby_power: "{{ am_standby_power }}"
|
||||||
|
operating_power_min: "{{ am_operating_power_min }}"
|
||||||
|
min_cycle_notify_seconds: "{{ am_min_cycle_notify_seconds }}"
|
||||||
|
|
||||||
|
announcement_channels: "{{ am_announcement_channels }}"
|
||||||
|
short_announcement: "{{ repeat.item.short_announcement }}"
|
||||||
|
long_announcement: "{{ am_long_announcement }}"
|
||||||
|
title: "{{ am_title }}"
|
||||||
|
matrix_indicator: "{{ repeat.item.matrix_indicator }}"
|
||||||
Reference in New Issue
Block a user