Files
KNELBMS/automations.yaml
T
mrcharles 239e449d45
ci / lint (push) Successful in 41s
feat(validate): post-deploy validation on every HA start [#778]
Missing/stale criticals after restart page with specifics instead of
waiting for humans; watchdog handles recovery; revert procedure
documented for repeat failures.
2026-09-04 16:23:53 -05:00

691 lines
25 KiB
YAML

- id: pfv_plant_ups_on_battery
alias: PFV plant - UPS on battery
triggers:
- trigger: state
entity_id:
- sensor.tsys1_ups_status
- sensor.tsys1_triplite_status
conditions:
- condition: template
value_template: '{{ ''On Battery'' in states(trigger.entity_id) }}'
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: POWER OUTAGE
url: /lovelace-pfv-server-room/ups
message: '{{ state_attr(trigger.entity_id, ''friendly_name'') }} lost utility
power and is ON BATTERY at {{ states(''sensor.'' ~ trigger.entity_id.split(''.'')[1]
| replace(''_status'', ''_battery_charge'')) }}% charge.'
mode: single
- id: pfv_plant_ups_battery_low
alias: PFV plant - UPS battery low
triggers:
- trigger: numeric_state
entity_id:
- sensor.tsys1_ups_battery_charge
- sensor.tsys1_triplite_battery_charge
below: 40
conditions:
- condition: template
value_template: '{{ ''On Battery'' in states(trigger.entity_id | replace(''_battery_charge'',
''_status'')) }}'
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: UPS BATTERY LOW
url: /lovelace-pfv-server-room/ups
message: '{{ trigger.entity_id }} at {{ states(trigger.entity_id) }}% while
on battery. Start graceful shutdown of VMs before hypervisors (#455 ordering).'
mode: single
- id: pfv_plant_ups_comm_lost
alias: PFV plant - UPS communication lost
triggers:
- trigger: state
entity_id:
- sensor.tsys1_ups_status
- sensor.tsys1_triplite_status
to: unavailable
for: 00:05:00
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: UPS COMMS LOST
url: /lovelace-pfv-server-room/ups
message: '{{ trigger.entity_id }} unreachable for 5 minutes (nut-server or network
path down; tsys1 also hosts pfv-bms itself).'
mode: single
- id: pfv_plant_ups_runtime_low
alias: PFV plant - UPS runtime low while on battery
description: On battery and runtime burned below 10 min — the graceful
shutdown window is closing. VMs before hypervisors per #455.
triggers:
- trigger: numeric_state
entity_id: sensor.pfv_ups_runtime_minutes
below: 10
conditions:
- condition: template
value_template: "{{ states('sensor.tsys1_ups_status') not in ['Online', 'unknown', 'unavailable'] }}"
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: UPS RUNTIME LOW
url: /lovelace-pfv-server-room/ups
message: On battery {{ states('sensor.pfv_ups_runtime_minutes') }} min left — start graceful shutdown now (#455 ordering).
mode: single
- id: pfv_plant_ups_power_restored
alias: PFV plant - utility power restored
triggers:
- trigger: state
entity_id:
- sensor.tsys1_ups_status
- sensor.tsys1_triplite_status
conditions:
- condition: template
value_template: "{{ trigger.from_state is not none and 'On Battery' in trigger.from_state.state\n
\ and 'On Battery' not in states(trigger.entity_id) }}"
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: Power restored
url: /lovelace-pfv-server-room/ups
message: '{{ state_attr(trigger.entity_id, ''friendly_name'') }} is back ONLINE.'
mode: single
- id: pfv_plant_high_temperature
alias: PFV plant - high temperature
triggers:
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_nvme_temperature
- sensor.pfv_tsys5_nvme_temperature
above: 150
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys4_dimm_18_temperature
- sensor.pfv_tsys4_dimm_19_temperature
- sensor.pfv_tsys4_dimm_1a_temperature
- sensor.pfv_tsys4_dimm_1b_temperature
above: 140
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys1_drive_temperature
- sensor.pfv_tsys4_drive_0_0_temperature
- sensor.pfv_tsys4_drive_0_10_temperature
- sensor.pfv_tsys4_drive_1_0_temperature
- sensor.pfv_tsys4_drive_1_10_temperature
- sensor.pfv_tsys5_drive_1_0_temperature
- sensor.pfv_tsys5_drive_4_10_temperature
- sensor.pfv_tsys5_drive_4_20_temperature
- sensor.pfv_tsys5_drive_4_30_temperature
- sensor.pfv_tsys9_drive_temperature
above: 140
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_gpu_temperature
- sensor.pfv_jetson_gpu_temperature
above: 185
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_pch_temperature
- sensor.pfv_tsys9_pch_temperature
above: 160
# CPU-SoC silicon tier: idle silicon routinely exceeds the
# ambient 104F threshold (subopi3 SoC idles at ~117F); 176F = 80C
# pre-throttle territory for Pi-Jetson SoCs and server CPUs [#733]
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_cpu_temperature
- sensor.pfv_tsys6_cpu_temperature
- sensor.pfv_tsys7_cpu_temperature
- sensor.pfv_tsys9_cpu_temperature
- sensor.pfv_tsys7_drac_cpu1
- sensor.pfv_tsys7_drac_cpu2
- sensor.pfv_jetson_temperature
- sensor.pfv_svrpi_temperature
- sensor.pfv_subopi3_temperature
- sensor.pfv_subopi_dev_3_temperature
- sensor.pfv_subopi_dev_4_temperature
above: 176
- trigger: numeric_state
entity_id:
- sensor.pfv_artroom_temperature
- sensor.pfv_garage_temperature
- sensor.pfv_tsys1_rack_temperature
- sensor.pfv_tsys4_ambient_temperature
- sensor.pfv_tsys5_ambient_temperature
- sensor.pfv_tsys6_drac_ambient
- sensor.pfv_tsys7_drac_inlet
- sensor.pfv_tsys7_drac_exhaust
above: 104
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: HIGH TEMPERATURE
url: /lovelace-pfv-server-room/temps
message: '{{ trigger.entity_id }} is {{ states(trigger.entity_id) }}°F (class
threshold exceeded). Check cooling/airflow.'
mode: single
- id: pfv_plant_sensor_watchdog
alias: PFV plant - sensor watchdog (unavailable sensors)
description: HA is the environmental brain (#344) — a silent sensor is an outage.
Re-alert suppressed by 4h cooldown via last_notified input_text if present.
triggers:
- trigger: time_pattern
minutes: /30
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: SENSOR WATCHDOG
url: /lovelace-pfv-server-room/overview
message: >-
{% set fleet = states.sensor | selectattr('entity_id', 'match', 'sensor\.pfv_')
| list %}
{% set dead = fleet | selectattr('state', 'in', ['unavailable', 'unknown']) | list %}
{% if dead | length > 0 %}
{{ dead | length }} of {{ fleet | length }} fleet sensors silent:
{{ dead | map(attribute='entity_id') | join(', ') }}
{% else %}
all {{ fleet | length }} fleet sensors OK
{% endif %}
mode: single
- id: pfv_plant_temp_rising_fast
alias: PFV plant - temperature rising fast (rate-of-change)
description: Catches dead AC minutes before the 104F threshold trips.
triggers:
- trigger: state
entity_id:
- binary_sensor.server_room_temp_rising
- binary_sensor.tsys6_ambient_rising
- binary_sensor.tsys7_inlet_rising
- binary_sensor.garage_temp_rising
to: "on"
for:
minutes: 2
actions:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: TEMP RISING FAST
url: /lovelace-pfv-server-room/temps
message: '{{ trigger.entity_id }} climbing ~10F/15min — check cooling
before the 104F threshold. (rate-of-change early warning)'
# Deploy pipeline [#734]: gitea repo webhook (release pushes) -> forced pull
# -> minimal reload. Core restarts ONLY when configuration.yaml or a
# packages platform-sensor file changed; everything else reloads with no
# bounce. The git_pull add-on's auto_restart is disabled - this webhook
# owns apply timing (no more 5-min-timer surprise restarts).
- id: pfv_deploy_git_webhook
alias: PFV deploy - git push to pull + minimal reload
description: >-
Gitea webhook on release pushes. Forces a git pull now, then
homeassistant.reload_all unless files that require a core restart
changed. Replaces the git_pull add-on timer auto-restart [#734].
mode: queued
max_exceeded: silent
triggers:
- trigger: webhook
webhook_id: !secret deploy_webhook_id
allowed_methods:
- POST
local_only: false
conditions: []
actions:
- if:
- condition: template
value_template: "{{ trigger.json.ref | default('') != 'refs/heads/release' }}"
then:
- stop: "deploy webhook: non-release ref {{ trigger.json.ref | default('') }} ignored"
- variables:
changed_files: >-
{{ (trigger.json.commits | default([]) | map(attribute='added') | flatten | list)
+ (trigger.json.commits | default([]) | map(attribute='removed') | flatten | list)
+ (trigger.json.commits | default([]) | map(attribute='modified') | flatten | list) }}
need_restart: >-
{{ changed_files | select('match', '^(configuration\.yaml|packages/.+)') | list | count > 0 }}
push_message: >-
Git push ({{ trigger.json.sender.login | default('unknown') }}):
{{ trigger.json.commits | default([]) | map(attribute='message') | join(' | ') | truncate(120) }}.
{{ 'Core restart required' if need_restart else 'Reload only - no core restart' }}.
- action: persistent_notification.create
data:
title: Deploy started
message: "{{ push_message }} Pulling now."
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: PFV deploy started
message: "{{ push_message }}"
- action: hassio.addon_restart
data:
addon: core_git_pull
- delay:
hours: 0
minutes: 0
seconds: 25
- if:
- condition: template
value_template: "{{ need_restart }}"
then:
- action: persistent_notification.create
data:
title: Deploy - restarting core
message: "Pull done; restarting core for {{ changed_files | join(', ') }}. Back in ~1 min."
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: PFV deploy - core restarting
message: "Pull done; restarting core for {{ changed_files | join(', ') }}."
- delay:
hours: 0
minutes: 0
seconds: 3
- action: homeassistant.restart
else:
- action: homeassistant.reload_all
- action: persistent_notification.create
data:
title: Deploy complete
message: "Pulled + reloaded YAML config at {{ now().strftime('%H:%M:%S') }} (no core restart). Files: {{ changed_files | join(', ') | truncate(180) }}"
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: PFV deploy complete
message: "Pulled + reloaded YAML at {{ now().strftime('%H:%M:%S') }} - no core restart. Files: {{ changed_files | join(', ') | truncate(120) }}"
# Deploy pipeline safety net [#734]: polls gitea release HEAD (rest sensor
# in packages/deploy_pipeline.yaml) and applies pulls reload-only. The
# gitea webhook is the fast path once gitea's SSRF allowlist permits
# pfv-bms.knel.net; until then this net catches every push within 5 min
# with NO core restart. Restarts for new platform sensors stay manual
# (22222 shell) until the webhook path is live.
- id: pfv_deploy_sha_watch
alias: PFV deploy - gitea sha watch safety net
description: >-
Release HEAD sha changed -> pull + CORE RESTART. Interim deploy
trigger while gitea webhook delivery is blocked by its SSRF host
allowlist [#734]. Restart (not reload) is REQUIRED: automation
reload loses webhook-trigger registration - found live 2026-09-03
(#345): after any reload-only deploy, webhook automations (doorman
scan ingest, this pipeline's own fast path) silently stop firing
while the endpoint still returns HTTP 200. Core restart
re-registers them. Accepted trade-off: ~1 min HA outage per
release deploy.
mode: single
max_exceeded: silent
triggers:
- trigger: state
entity_id: sensor.pfv_deploy_gitea_release_head
conditions:
- condition: template
value_template: >-
{{ trigger.from_state is not none
and trigger.from_state.state not in ['unknown', 'unavailable']
and trigger.to_state is not none
and trigger.from_state.state != trigger.to_state.state }}
actions:
- action: hassio.addon_restart
data:
addon: core_git_pull
- delay:
hours: 0
minutes: 0
seconds: 20
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: PFV deploy (sha watch)
message: >-
New release HEAD {{ states('sensor.pfv_deploy_gitea_release_head')[:7] }}
pulled. Restarting HA core (webhook re-registration) - back in
~1 min.
- action: homeassistant.restart
- id: pfv_plant_temperature_recovered
alias: PFV plant - temperature recovered
description: >-
Recovery twin of the high-temperature alert [#344 ask 2026-09-03]:
notify when a sensor that was OVER its tier drops back below
(fire-line minus 5F hysteresis) and HOLDS there for 10 minutes.
from_state guard prevents spurious fires at HA restart; the tier
map mirrors the high-temperature automation thresholds exactly.
triggers:
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_nvme_temperature
- sensor.pfv_tsys5_nvme_temperature
below: 145
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys4_dimm_18_temperature
- sensor.pfv_tsys4_dimm_19_temperature
- sensor.pfv_tsys4_dimm_1a_temperature
- sensor.pfv_tsys4_dimm_1b_temperature
below: 135
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys1_drive_temperature
- sensor.pfv_tsys4_drive_0_0_temperature
- sensor.pfv_tsys4_drive_0_10_temperature
- sensor.pfv_tsys4_drive_1_0_temperature
- sensor.pfv_tsys4_drive_1_10_temperature
- sensor.pfv_tsys5_drive_1_0_temperature
- sensor.pfv_tsys5_drive_4_10_temperature
- sensor.pfv_tsys5_drive_4_20_temperature
- sensor.pfv_tsys5_drive_4_30_temperature
- sensor.pfv_tsys9_drive_temperature
below: 135
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_gpu_temperature
- sensor.pfv_jetson_gpu_temperature
below: 180
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_pch_temperature
- sensor.pfv_tsys9_pch_temperature
below: 155
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_tsys3_cpu_temperature
- sensor.pfv_tsys6_cpu_temperature
- sensor.pfv_tsys7_cpu_temperature
- sensor.pfv_tsys9_cpu_temperature
- sensor.pfv_tsys7_drac_cpu1
- sensor.pfv_tsys7_drac_cpu2
- sensor.pfv_jetson_temperature
- sensor.pfv_svrpi_temperature
- sensor.pfv_subopi3_temperature
- sensor.pfv_subopi_dev_3_temperature
- sensor.pfv_subopi_dev_4_temperature
below: 171
for: 00:10:00
- trigger: numeric_state
entity_id:
- sensor.pfv_artroom_temperature
- sensor.pfv_garage_temperature
- sensor.pfv_tsys1_rack_temperature
- sensor.pfv_tsys4_ambient_temperature
- sensor.pfv_tsys5_ambient_temperature
- sensor.pfv_tsys6_drac_ambient
- sensor.pfv_tsys7_drac_inlet
- sensor.pfv_tsys7_drac_exhaust
below: 99
for: 00:10:00
actions:
- variables:
fire_lines:
sensor.pfv_tsys3_nvme_temperature: 150
sensor.pfv_tsys5_nvme_temperature: 150
sensor.pfv_tsys4_dimm_18_temperature: 140
sensor.pfv_tsys4_dimm_19_temperature: 140
sensor.pfv_tsys4_dimm_1a_temperature: 140
sensor.pfv_tsys4_dimm_1b_temperature: 140
sensor.pfv_tsys1_drive_temperature: 140
sensor.pfv_tsys4_drive_0_0_temperature: 140
sensor.pfv_tsys4_drive_0_10_temperature: 140
sensor.pfv_tsys4_drive_1_0_temperature: 140
sensor.pfv_tsys4_drive_1_10_temperature: 140
sensor.pfv_tsys5_drive_1_0_temperature: 140
sensor.pfv_tsys5_drive_4_10_temperature: 140
sensor.pfv_tsys5_drive_4_20_temperature: 140
sensor.pfv_tsys5_drive_4_30_temperature: 140
sensor.pfv_tsys9_drive_temperature: 140
sensor.pfv_tsys3_gpu_temperature: 185
sensor.pfv_jetson_gpu_temperature: 185
sensor.pfv_tsys3_pch_temperature: 160
sensor.pfv_tsys9_pch_temperature: 160
sensor.pfv_tsys3_cpu_temperature: 176
sensor.pfv_tsys6_cpu_temperature: 176
sensor.pfv_tsys7_cpu_temperature: 176
sensor.pfv_tsys9_cpu_temperature: 176
sensor.pfv_tsys7_drac_cpu1: 176
sensor.pfv_tsys7_drac_cpu2: 176
sensor.pfv_jetson_temperature: 176
sensor.pfv_svrpi_temperature: 176
sensor.pfv_subopi3_temperature: 176
sensor.pfv_subopi_dev_3_temperature: 176
sensor.pfv_subopi_dev_4_temperature: 176
sensor.pfv_artroom_temperature: 104
sensor.pfv_garage_temperature: 104
sensor.pfv_tsys1_rack_temperature: 104
sensor.pfv_tsys4_ambient_temperature: 104
sensor.pfv_tsys5_ambient_temperature: 104
sensor.pfv_tsys6_drac_ambient: 104
sensor.pfv_tsys7_drac_inlet: 104
sensor.pfv_tsys7_drac_exhaust: 104
fire: "{{ fire_lines.get(trigger.entity_id, 0) | int }}"
- condition: template
value_template: >-
{{ trigger.from_state is not none
and trigger.from_state.state not in ['unavailable', 'unknown', 'none']
and trigger.from_state.state | float(-1) >= fire }}
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: TEMPERATURE RECOVERED
url: /lovelace-pfv-server-room/temps
message: >-
{{ state_attr(trigger.entity_id, 'friendly_name') | default(trigger.entity_id) }}
back to {{ states(trigger.entity_id) }}°F and holding — was
{{ trigger.from_state.state }}°F when the alert cleared (tier fire line {{ fire }}°F).
mode: single
- id: pfv_watchdog_stale_entities
alias: PFV watchdog - stale entity auto-heal
description: >
Detects silent poller wedges (2026-09-04: snmp tsys9 went 12h
undetected; NUT APC 3h) and auto-heals config-entry platforms via
homeassistant.reload_config_entry — no restart needed. Escalates to
notify when reload does not recover. Auto core restart ONLY while
input_boolean.pfv_watchdog_auto_restart is ON and entities stay
stale — restart costs ~1 min downtime and re-registers webhooks
(#345, #789). v1 notify repeats each cycle while stale (visible
alarm); throttle in v2.
mode: single
max_exceeded: silent
triggers:
- trigger: time_pattern
minutes: /5
variables:
checks:
- [sensor.pfv_tsys1_rack_temperature, 20]
- [sensor.pfv_tsys1_drive_temperature, 30]
- [sensor.pfv_tsys3_cpu_temperature, 20]
- [sensor.pfv_tsys4_ambient_temperature, 20]
- [sensor.pfv_tsys6_cpu_temperature, 20]
- [sensor.pfv_tsys6_drac_ambient, 30]
- [sensor.pfv_tsys7_cpu_temperature, 20]
- [sensor.pfv_tsys7_drac_inlet, 30]
- [sensor.pfv_tsys9_cpu_temperature, 20]
- [sensor.pfv_tsys9_drive_temperature, 30]
- [sensor.tsys1_ups_status, 15]
- [sensor.tsys1_triplite_status, 15]
- [sensor.pfv_deploy_gitea_release_head, 30]
actions:
- variables:
stale_list: >-
{% set ns = namespace(stale=[]) %}
{% for pair in checks %}
{% set s = states[pair[0]] %}
{% if s is not none and s.last_updated is defined
and (now() - s.last_updated).total_seconds() > pair[1] * 60 %}
{% set ns.stale = ns.stale + [pair[0]] %}
{% endif %}
{% endfor %}
{{ ns.stale }}
- condition: template
value_template: "{{ stale_list | length > 0 }}"
- repeat:
for_each: "{{ stale_list }}"
sequence:
- action: homeassistant.reload_config_entry
data:
entity_id: "{{ repeat.item }}"
- delay:
hours: 0
minutes: 2
seconds: 0
- variables:
still_stale: >-
{% set ns = namespace(stale=[]) %}
{% for pair in checks %}
{% set s = states[pair[0]] %}
{% if s is not none and s.last_updated is defined
and (now() - s.last_updated).total_seconds() > pair[1] * 60 %}
{% set ns.stale = ns.stale + [pair[0]] %}
{% endif %}
{% endfor %}
{{ ns.stale }}
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: WATCHDOG
url: /lovelace-pfv-server-room/temps
message: >-
Stale after auto-reload: {{ still_stale | join(', ') }}.
Auto-restart is {{ 'ON' if is_state('input_boolean.pfv_watchdog_auto_restart','on')
else 'OFF (arm it on the watchdog switch if you want unattended restarts)' }}.
- if:
- condition: template
value_template: "{{ is_state('input_boolean.pfv_watchdog_auto_restart','on') }}"
then:
- delay:
hours: 0
minutes: 30
seconds: 0
- variables:
hard_stale: >-
{% set ns = namespace(stale=[]) %}
{% for pair in checks %}
{% set s = states[pair[0]] %}
{% if s is not none and s.last_updated is defined
and (now() - s.last_updated).total_seconds() > pair[1] * 60 %}
{% set ns.stale = ns.stale + [pair[0]] %}
{% endif %}
{% endfor %}
{{ ns.stale }}
- if:
- condition: template
value_template: "{{ hard_stale | length > 0 }}"
then:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: WATCHDOG — restarting core
message: "Still stale after reload + 30 min: {{ hard_stale | join(', ') }}. Restarting HA core now."
- action: homeassistant.restart
- id: pfv_post_deploy_validation
alias: PFV post-deploy validation
description: >
Runs after EVERY HA start (deploy restarts included): after a settle
delay, verifies the critical sensor set actually materialized and is
fresh. Catches bad deploys — missing templates, broken packages,
wedged pollers — and pages with specifics instead of waiting for a
human to notice (#778, #777). The watchdog then handles recovery.
mode: single
max_exceeded: silent
triggers:
- trigger: event
event_type: homeassistant_start
actions:
- delay:
hours: 0
minutes: 3
seconds: 0
- variables:
required:
- [sensor.pfv_tsys1_rack_temperature, 20]
- [sensor.pfv_tsys9_cpu_temperature, 20]
- [sensor.tsys1_ups_status, 15]
- [sensor.tsys1_triplite_status, 15]
- [sensor.pfv_ups_runtime_minutes, 30]
- [sensor.pfv_tripp_lite_runtime_minutes, 30]
- [sensor.pfv_deploy_gitea_release_head, 30]
- [automation.pfv_watchdog_stale_entities, 0]
- variables:
bad_list: >-
{% set ns = namespace(bad=[]) %}
{% for pair in required %}
{% set s = states[pair[0]] %}
{% if s is none or s.state in ['unknown', 'unavailable', 'none'] %}
{% set ns.bad = ns.bad + [pair[0] ~ ' (missing)'] %}
{% elif pair[1] > 0 and s.last_updated is defined
and (now() - s.last_updated).total_seconds() > pair[1] * 60 %}
{% set ns.bad = ns.bad + [pair[0] ~ ' (stale)'] %}
{% endif %}
{% endfor %}
{{ ns.bad }}
- if:
- condition: template
value_template: "{{ bad_list | length > 0 }}"
then:
- action: notify.send_message
target:
entity_id:
- notify.ultix_sidecar_new
- notify.allthes_ipad
data:
title: POST-DEPLOY VALIDATION FAILED
url: /lovelace-pfv-thermal
message: >-
After restart, these criticals are missing/stale:
{{ bad_list | join(', ') }}. Watchdog will retry recovery;
if this repeats, revert the last release (git revert + push).
- action: persistent_notification.create
data:
title: POST-DEPLOY VALIDATION FAILED
message: "Missing/stale: {{ bad_list | join(', ') }}"