################################################################################ # 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 }}"