From f4cf8c7d0bb12c1f396d2c16004023d476bf5dd0 Mon Sep 17 00:00:00 2001 From: Carlo Costanzo Date: Tue, 1 Sep 2026 00:11:13 -0400 Subject: [PATCH] Refine maintenance and infrastructure operations --- config/README.md | 2 +- .../dashboards/infrastructure/dashboard.yaml | 2 +- .../partials/home_sections.yaml | 156 ++++++++++- .../partials/home_water_sections.yaml | 141 ++-------- .../partials/hvac_sections.yaml | 262 ++++++++++++++++++ .../templates/button_card_templates.yaml | 60 +++- .../infrastructure/views/01_home_water.yaml | 6 +- .../infrastructure/views/01_hvac.yaml | 22 ++ .../overview/sections/00_summary.yaml | 5 +- .../dashboards/overview/views/02_mobile.yaml | 3 + config/logbook.yaml | 197 +------------ config/logger.yaml | 2 + config/packages/README.md | 9 +- config/packages/climate.yaml | 240 +++++++++++++++- config/packages/docker_infrastructure.yaml | 71 ++--- config/packages/infrastructure.yaml | 45 ++- config/packages/maintenance_log.yaml | 43 ++- config/packages/tugtainer_updates.yaml | 20 +- config/recorder.yaml | 197 +------------ 19 files changed, 876 insertions(+), 607 deletions(-) create mode 100644 config/dashboards/infrastructure/partials/hvac_sections.yaml create mode 100644 config/dashboards/infrastructure/views/01_hvac.yaml diff --git a/config/README.md b/config/README.md index f8548e5b..60de8318 100755 --- a/config/README.md +++ b/config/README.md @@ -32,7 +32,7 @@ Live view of the `config/` directory my production Home Assistant instance loads ### How the config is organized - **Packages (`packages/`)** – complete subsystems that bundle sensors, switches, automations, scripts, and lovelace assets for a single feature (alarm, garage, water shutoff, etc.). -- **Container updates** – [![YAML source: tugtainer_updates](https://img.shields.io/static/v1?label=YAML&message=tugtainer_updates&color=lightgrey&logo=github&logoColor=181717)](packages/tugtainer_updates.yaml) logs Tugtainer reports into Home Assistant persistent notifications and dispatches controlled review work. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/4NNOkXzUyYw) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/home-assistant-tugtainer-docker-updates.html) +- **Container updates** – [![YAML source: tugtainer_updates](https://img.shields.io/static/v1?label=YAML&message=tugtainer_updates&color=lightgrey&logo=github&logoColor=181717)](packages/tugtainer_updates.yaml) processes Available reports without persistent alerts, keeps Updated/Failed outcomes visible, and dispatches controlled review work. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/4NNOkXzUyYw) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/home-assistant-tugtainer-docker-updates.html) - **WAN monitoring** – [![YAML source: infrastructure](https://img.shields.io/static/v1?label=YAML&message=infrastructure&color=lightgrey&logo=github&logoColor=181717)](packages/infrastructure.yaml), [![YAML source: speedtest](https://img.shields.io/static/v1?label=YAML&message=speedtest&color=lightgrey&logo=github&logoColor=181717)](packages/speedtest.yaml), and the [WAN dashboard](dashboards/infrastructure/views/04_wan.yaml) separate multi-probe reachability, continuous latency/loss, Ookla capacity, and result freshness. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/F-PpsKkzcrM) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/08/home-assistant-internet-speed-monitoring-ookla.html) - **Entity cleanup** – [![YAML source: docker_infrastructure](https://img.shields.io/static/v1?label=YAML&message=docker_infrastructure&color=lightgrey&logo=github&logoColor=181717)](packages/docker_infrastructure.yaml), [![YAML source: infrastructure](https://img.shields.io/static/v1?label=YAML&message=infrastructure&color=lightgrey&logo=github&logoColor=181717)](packages/infrastructure.yaml), [recorder](recorder.yaml), and [logbook](logbook.yaml) show how the Portainer and Glances audit kept proven history while removing stale registry noise. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/aceV7ObKJVA) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/using-codex-clean-up-home-assistant-entities.html) - **Automations (`automation/`)** – event-driven YAML broken out by area or device; the legacy `automations.yaml` remains for UI-created flows. diff --git a/config/dashboards/infrastructure/dashboard.yaml b/config/dashboards/infrastructure/dashboard.yaml index cf41c3d9..15fcdc8f 100644 --- a/config/dashboards/infrastructure/dashboard.yaml +++ b/config/dashboards/infrastructure/dashboard.yaml @@ -7,7 +7,7 @@ # Operational drill-downs for house utilities, appliances, and infrastructure. # ------------------------------------------------------------------- # Notes: Stable dashboard key/path remains `dashboard-infrastructure`; the user-facing name is Systems. -# Notes: Home Water and Vacuum are promoted production views; Health remains on Overview. +# Notes: Water, HVAC, and Vacuum are promoted production views; Health remains on Overview. # Notes: Joanna dispatch telemetry view is available at `/dashboard-infrastructure/joanna`. # Notes: Network combines WAN, dual Pi-hole, and website health; `/wan`, `/pihole`, and `/website-health` are detail subviews. ###################################################################### diff --git a/config/dashboards/infrastructure/partials/home_sections.yaml b/config/dashboards/infrastructure/partials/home_sections.yaml index a3b7d286..c85fc4a7 100644 --- a/config/dashboards/infrastructure/partials/home_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_sections.yaml @@ -6,15 +6,15 @@ # Systems Partial - operations summary # House-system health strip, active issues, and datacenter status. # ------------------------------------------------------------------- -# Related Issue: 1890 +# Related Issue: 1890, 1817 # Notes: The health ribbon mirrors the visible dashboard navigation order and labels. # Notes: DNS and external-monitoring faults roll up under Network instead of duplicating it. # Notes: Network trend panels live on Network so Overview stays focused on broad house status. # Notes: Power & Solar shows realtime flow; accumulated history remains in Energy. -# Notes: Scheduled Proxmox maintenance and sustained WAN quality/reset warnings render amber; critical conditions remain red. +# Notes: Scheduled Proxmox, Docker host, and HVAC maintenance plus sustained WAN quality/reset warnings render amber; critical conditions remain red. # Notes: Active Issue rows enumerate faults, scheduled maintenance, and reset urgency so drill-down is optional for initial triage. # Notes: BearClaw scheduled-job warnings/errors roll up under Joanna and appear as an exceptions-only Active Issue row. -# Notes: Home Water and Systems surface normalized Rheem faults; telemetry-only loss renders amber. +# Notes: Water and Systems surface normalized Rheem faults; telemetry-only loss renders amber. # Notes: Back to Overview is the first control in the landing-view navigation strip. ###################################################################### @@ -31,7 +31,7 @@ columns: full layout_type: custom:grid-layout layout: - grid-template-columns: repeat(10, minmax(0, 1fr)) + grid-template-columns: repeat(6, minmax(0, 1fr)) grid-auto-flow: row grid-auto-rows: min-content grid-gap: 12px @@ -58,11 +58,19 @@ variables: ok_state: '0' healthy_text: ALL CLEAR - warning_text: WARNING + warning_text: > + [[[ + return states['binary_sensor.hvac_maintenance_due']?.state === 'on' + || states['binary_sensor.docker_host_maintenance_attention']?.state === 'on' + ? 'MAINTENANCE' + : 'WARNING'; + ]]] raw_suffix: ACTIVE warning_entity_ids: - binary_sensor.infra_wan_sustained_degradation - binary_sensor.rheem_wh_telemetry_stale + - binary_sensor.docker_host_maintenance_attention + - binary_sensor.hvac_maintenance_due critical_entity_ids: - binary_sensor.infra_wan_critical - binary_sensor.infra_dns_pihole_degraded @@ -75,7 +83,7 @@ - type: custom:button-card template: bearstone_infra_status_kpi entity: binary_sensor.home_water_problem - name: Home Water + name: Water icon: mdi:shield-water-outline variables: ok_state: 'off' @@ -91,6 +99,21 @@ action: navigate navigation_path: /dashboard-infrastructure/home-water + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.hvac_maintenance_due + name: HVAC + icon: mdi:hvac + variables: + ok_state: 'off' + healthy_text: CLEAR + warning_text: MAINTENANCE + warning_entity_ids: + - binary_sensor.hvac_maintenance_due + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/hvac + - type: custom:button-card template: bearstone_infra_status_kpi entity: sensor.l10s_vacuum_error @@ -125,6 +148,11 @@ icon: mdi:docker variables: ok_state: 'off' + warning_text: MAINTENANCE + warning_entity_ids: + - binary_sensor.docker_host_maintenance_attention + critical_entity_ids: + - binary_sensor.docker_container_telemetry_degraded tap_action: action: navigate navigation_path: /dashboard-infrastructure/docker @@ -309,7 +337,70 @@ state_display: '[[[ return "LEAK"; ]]]' tap_action: action: navigate - navigation_path: /dashboard-infrastructure/home-water + navigation_path: /dashboard-infrastructure/hvac + + - type: custom:button-card + template: bearstone_infra_alert_row + entity: sensor.downstairs_ac_runtime_since_last_filter_change + name: Downstairs HVAC filter maintenance due + icon: mdi:air-filter + variables: + alert_kind: numeric_high + threshold: 550 + warning_only: true + exception_detail: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) + ? `RUNTIME ${value.toFixed(0)} / 550 H · FILTER CHANGE DUE` + : 'FILTER RUNTIME TELEMETRY UNAVAILABLE'; + ]]] + state_display: MAINTENANCE + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/hvac + + - type: custom:button-card + template: bearstone_infra_alert_row + entity: sensor.upstairs_ac_runtime_since_last_filter_change + name: Upstairs HVAC filter maintenance due + icon: mdi:air-filter + variables: + alert_kind: numeric_high + threshold: 450 + warning_only: true + exception_detail: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) + ? `RUNTIME ${value.toFixed(0)} / 450 H · FILTER CHANGE DUE` + : 'FILTER RUNTIME TELEMETRY UNAVAILABLE'; + ]]] + state_display: MAINTENANCE + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/hvac + + - type: custom:button-card + template: bearstone_infra_alert_row + entity: sensor.hvac_condenser_lines_days_since_cleaned + name: HVAC condenser line maintenance due + icon: mdi:pipe-wrench + variables: + alert_kind: numeric_high + threshold: 360 + warning_only: true + exception_detail: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) + ? `ELAPSED ${value.toFixed(0)} / 360 DAYS · LINE CLEANING DUE` + : 'CONDENSER MAINTENANCE TELEMETRY UNAVAILABLE'; + ]]] + state_display: MAINTENANCE + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/hvac - type: custom:button-card template: bearstone_infra_alert_row @@ -441,6 +532,57 @@ action: navigate navigation_path: /dashboard-infrastructure/proxmox + - type: custom:button-card + template: bearstone_infra_alert_row + entity: binary_sensor.docker_host_maintenance_attention + name: Docker host maintenance needs attention + icon: mdi:package-up + variables: + alert_kind: binary_on + warning_only: true + exception_detail: > + [[[ + const hosts = [ + ['docker_10', 'sensor.docker_10_apt_status'], + ['docker_13', 'sensor.docker_13_apt_status'], + ['docker_14', 'sensor.docker_14_apt_status'], + ['docker_17', 'sensor.docker_17_apt_status'], + ['docker_69', 'sensor.docker_69_apt_status'], + ]; + const needsAttention = (raw) => { + const status = String(raw ?? '').toUpperCase(); + return ['UNKNOWN', 'UNAVAILABLE', 'NONE', ''].includes(status) + || status.includes('PENDING') + || status.includes('REBOOT REQ') + || status.startsWith('ERROR'); + }; + return hosts + .filter(([, id]) => needsAttention(states[id]?.state)) + .map(([host, id]) => `${host} ${String(states[id]?.state ?? 'unknown').toUpperCase()}`) + .join(' · '); + ]]] + state_display: > + [[[ + const ids = [ + 'sensor.docker_10_apt_status', + 'sensor.docker_13_apt_status', + 'sensor.docker_14_apt_status', + 'sensor.docker_17_apt_status', + 'sensor.docker_69_apt_status', + ]; + const count = ids.filter((id) => { + const status = String(states[id]?.state ?? '').toUpperCase(); + return ['UNKNOWN', 'UNAVAILABLE', 'NONE', ''].includes(status) + || status.includes('PENDING') + || status.includes('REBOOT REQ') + || status.startsWith('ERROR'); + }).length; + return `${count} HOST${count === 1 ? '' : 'S'}`; + ]]] + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/docker + - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.docker_container_telemetry_degraded diff --git a/config/dashboards/infrastructure/partials/home_water_sections.yaml b/config/dashboards/infrastructure/partials/home_water_sections.yaml index 0df3f83e..fd0b426d 100644 --- a/config/dashboards/infrastructure/partials/home_water_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_water_sections.yaml @@ -3,17 +3,17 @@ # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- -# Systems Partial - Home Water -# Protection, live use, hot water, maintenance, outdoor water, and trends. +# Systems Partial - Water +# Protection, live use, hot water, salt, outdoor water, and trends. # ------------------------------------------------------------------- -# Related Issue: 1890 +# Related Issue: 1890, 1817 # Notes: NAS, NVR, and unrelated house-temperature cards are omitted; refrigerator temperature stays with home safety. -# Notes: Maintenance rows reuse existing reset scripts through safe more-info flows. +# Notes: HVAC maintenance has moved to the dedicated production HVAC view. # Notes: Active Phyn leak alerts are shown; the integration's stale latest-test leak flag is not an operator alert. # Notes: Rheem alert, health, and telemetry faults render as an exception row backed by durable Repairs. # Notes: Daily usage uses change on the same sum-only statistic configured in Energy. -# Notes: The overview ribbon prioritizes the next maintenance action and frequent controls without hidden grid placeholders. -# Notes: Refrigerator temperature remains a temporary Home Maintenance overview item until a better permanent view is chosen. +# Notes: The overview ribbon keeps water-softener salt maintenance visible and amber when due. +# Notes: Refrigerator temperature remains a temporary Home Safety overview item until a better permanent view is chosen. ###################################################################### # ------------------------------------------------------------------- @@ -39,70 +39,25 @@ grid-template-columns: repeat(2, minmax(0, 1fr)) cards: - type: custom:button-card - template: bearstone_infra_kpi - entity: sensor.downstairs_ac_runtime_since_last_filter_change - name: Next Maintenance - icon: mdi:calendar-clock + template: bearstone_infra_status_kpi + entity: sensor.water_softener_salt_days_since_last_add + name: Salt Maintenance + icon: mdi:shaker-outline variables: - next_maintenance: > - [[[ - const candidates = []; - const addCandidate = (entityId, label, current, limit, unit) => { - if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) return; - const remaining = Math.ceil(limit - current); - candidates.push({ - entity: entityId, - progress: current / limit, - text: remaining <= 0 ? `DUE · ${label}` : `${label} · ${remaining}${unit}`, - }); - }; - - addCandidate( - 'sensor.downstairs_ac_runtime_since_last_filter_change', - 'DOWNSTAIRS', - Number(states['sensor.downstairs_ac_runtime_since_last_filter_change']?.state), - 350, - 'H', - ); - addCandidate( - 'sensor.upstairs_ac_runtime_since_last_filter_change', - 'UPSTAIRS', - Number(states['sensor.upstairs_ac_runtime_since_last_filter_change']?.state), - 450, - 'H', - ); - addCandidate( - 'sensor.hvac_condenser_lines_days_since_cleaned', - 'CONDENSER', - Number(states['sensor.hvac_condenser_lines_days_since_cleaned']?.state), - 360, - 'D', - ); - addCandidate( - 'sensor.water_softener_salt_days_since_last_add', - 'SALT', - Number(states['sensor.water_softener_salt_days_since_last_add']?.state), - Number(states['sensor.water_softener_salt_average_days_between_refills']?.state), - 'D', - ); - - candidates.sort((a, b) => b.progress - a.progress); - return candidates[0] ?? { entity: entity.entity_id, text: 'NO DATA' }; - ]]] + default_severity: healthy + numeric_warning_checks: + - entity_id: sensor.water_softener_salt_days_since_last_add + threshold_entity_id: sensor.water_softener_salt_average_days_between_refills state_display: > [[[ - const text = variables?.next_maintenance?.text ?? 'NO DATA'; - return text - .replace('DOWNSTAIRS', 'DOWN') - .replace('UPSTAIRS', 'UP') - .replace('CONDENSER', 'COND'); + const current = Number(entity?.state); + const limit = Number(states['sensor.water_softener_salt_average_days_between_refills']?.state); + if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) return 'NO DATA'; + const remaining = Math.ceil(limit - current); + return remaining <= 0 ? 'DUE · SALT' : `${remaining}D LEFT`; ]]] tap_action: action: more-info - entity: > - [[[ - return variables?.next_maintenance?.entity ?? entity.entity_id; - ]]] - type: custom:button-card template: bearstone_infra_status_kpi @@ -175,7 +130,7 @@ url_path: https://www.readyrefresh.com/en/login # ------------------------------------------------------------------- -# Home maintenance and outdoor water +# Home safety and outdoor water # ------------------------------------------------------------------- - type: grid column_span: 4 @@ -202,7 +157,7 @@ cards: - type: custom:button-card template: bearstone_infra_panel_header - name: Home Maintenance + name: Home Safety - type: custom:button-card template: bearstone_infra_list_row @@ -222,60 +177,6 @@ - color: rgba(198,40,40,1) - opacity: 1 - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.downstairs_ac_runtime_since_last_filter_change - name: Downstairs Filter Runtime - icon: mdi:air-filter - state_display: > - [[[ - const value = Number(entity?.state); - return Number.isFinite(value) ? `${value.toFixed(0)} / 350 h` : 'NO DATA'; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: input_datetime.downstairs_last_filter_change - name: Downstairs Filter Changed - icon: mdi:calendar-check-outline - tap_action: - action: more-info - entity: script.reset_downstairs_filter - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.upstairs_ac_runtime_since_last_filter_change - name: Upstairs Filter Runtime - icon: mdi:air-filter - state_display: > - [[[ - const value = Number(entity?.state); - return Number.isFinite(value) ? `${value.toFixed(0)} / 450 h` : 'NO DATA'; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: input_datetime.upstairs_last_filter_change - name: Upstairs Filter Changed - icon: mdi:calendar-check-outline - tap_action: - action: more-info - entity: script.reset_upstairs_filter - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.hvac_condenser_lines_days_since_cleaned - name: Condenser Lines - icon: mdi:pipe-wrench - state_display: > - [[[ - const value = Number(entity?.state); - return Number.isFinite(value) ? `${value.toFixed(0)} / 360 DAYS` : 'NO DATA'; - ]]] - tap_action: - action: more-info - entity: script.reset_hvac_condenser_lines_cleaned - - type: custom:vertical-stack-in-card card_mod: style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml diff --git a/config/dashboards/infrastructure/partials/hvac_sections.yaml b/config/dashboards/infrastructure/partials/hvac_sections.yaml new file mode 100644 index 00000000..c2fa8ee3 --- /dev/null +++ b/config/dashboards/infrastructure/partials/hvac_sections.yaml @@ -0,0 +1,262 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Systems HVAC Partial - Maintenance overview and details +# Production HVAC maintenance, completion history, and reset access. +# ------------------------------------------------------------------- +# Related Issue: 1817 +# Notes: Downstairs filter maintenance is due at 550 runtime hours; upstairs remains 450 hours. +# Notes: Reset actions reuse the existing HVAC maintenance scripts; no staging helpers are introduced. +###################################################################### + +- type: grid + column_span: 4 + columns: 1 + square: false + cards: + - type: custom:layout-card + grid_options: + columns: full + layout_type: custom:grid-layout + layout: + grid-template-columns: repeat(4, minmax(0, 1fr)) + grid-auto-flow: row + grid-auto-rows: min-content + grid-gap: 12px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_nav_back + name: Back to Systems + icon: mdi:arrow-left + view_layout: + grid-column: 1 / -1 + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/home + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: sensor.downstairs_ac_runtime_since_last_filter_change + name: Next Maintenance + icon: mdi:calendar-clock + variables: + default_severity: healthy + numeric_warning_checks: + - entity_id: sensor.downstairs_ac_runtime_since_last_filter_change + threshold: 550 + - entity_id: sensor.upstairs_ac_runtime_since_last_filter_change + threshold: 450 + - entity_id: sensor.hvac_condenser_lines_days_since_cleaned + threshold: 360 + next_maintenance: > + [[[ + const candidates = []; + const addCandidate = (entityId, label, current, limit, unit) => { + if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) return; + const remaining = Math.ceil(limit - current); + candidates.push({ + entity: entityId, + progress: current / limit, + text: remaining <= 0 ? `DUE · ${label}` : `${label} · ${remaining}${unit}`, + }); + }; + addCandidate( + 'sensor.downstairs_ac_runtime_since_last_filter_change', + 'DOWNSTAIRS', + Number(states['sensor.downstairs_ac_runtime_since_last_filter_change']?.state), + 550, + 'H', + ); + addCandidate( + 'sensor.upstairs_ac_runtime_since_last_filter_change', + 'UPSTAIRS', + Number(states['sensor.upstairs_ac_runtime_since_last_filter_change']?.state), + 450, + 'H', + ); + addCandidate( + 'sensor.hvac_condenser_lines_days_since_cleaned', + 'CONDENSER', + Number(states['sensor.hvac_condenser_lines_days_since_cleaned']?.state), + 360, + 'D', + ); + candidates.sort((a, b) => b.progress - a.progress); + return candidates[0] ?? { entity: entity.entity_id, text: 'NO DATA' }; + ]]] + state_display: > + [[[ + return String(variables?.next_maintenance?.text ?? 'NO DATA') + .replace('DOWNSTAIRS', 'DOWN') + .replace('UPSTAIRS', 'UP') + .replace('CONDENSER', 'COND'); + ]]] + tap_action: + action: more-info + entity: > + [[[ + return variables?.next_maintenance?.entity ?? entity.entity_id; + ]]] + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: sensor.downstairs_ac_runtime_since_last_filter_change + name: Downstairs Filter + icon: mdi:air-filter + variables: + ok_state: never + default_severity: healthy + numeric_warning_checks: + - entity_id: sensor.downstairs_ac_runtime_since_last_filter_change + threshold: 550 + warning_text: DUE + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 550 H` : 'NO DATA'; + ]]] + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: sensor.upstairs_ac_runtime_since_last_filter_change + name: Upstairs Filter + icon: mdi:air-filter + variables: + ok_state: never + default_severity: healthy + numeric_warning_checks: + - entity_id: sensor.upstairs_ac_runtime_since_last_filter_change + threshold: 450 + warning_text: DUE + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 450 H` : 'NO DATA'; + ]]] + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: sensor.hvac_condenser_lines_days_since_cleaned + name: Condenser Lines + icon: mdi:pipe-wrench + variables: + ok_state: never + default_severity: healthy + numeric_warning_checks: + - entity_id: sensor.hvac_condenser_lines_days_since_cleaned + threshold: 360 + warning_text: DUE + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 360 D` : 'NO DATA'; + ]]] + +- type: grid + column_span: 4 + columns: 1 + square: false + cards: + - type: custom:layout-card + grid_options: + columns: full + layout_type: custom:grid-layout + layout: + grid-template-columns: repeat(2, minmax(0, 1fr)) + grid-auto-flow: row + grid-auto-rows: min-content + grid-gap: 12px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(1, minmax(0, 1fr)) + cards: + - type: custom:vertical-stack-in-card + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Filter Maintenance + + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.downstairs_ac_runtime_since_last_filter_change + name: Downstairs Filter Runtime + icon: mdi:air-filter + variables: + numeric_warning_threshold: 550 + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 550 h` : 'NO DATA'; + ]]] + + - type: custom:button-card + template: bearstone_infra_list_row + entity: input_datetime.downstairs_last_filter_change + name: Downstairs Filter Changed + icon: mdi:calendar-check-outline + tap_action: + action: more-info + entity: script.reset_downstairs_filter + + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.upstairs_ac_runtime_since_last_filter_change + name: Upstairs Filter Runtime + icon: mdi:air-filter + variables: + numeric_warning_threshold: 450 + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 450 h` : 'NO DATA'; + ]]] + + - type: custom:button-card + template: bearstone_infra_list_row + entity: input_datetime.upstairs_last_filter_change + name: Upstairs Filter Changed + icon: mdi:calendar-check-outline + tap_action: + action: more-info + entity: script.reset_upstairs_filter + + - type: custom:vertical-stack-in-card + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Outdoor Condenser + + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.hvac_condenser_lines_days_since_cleaned + name: Days Since Line Cleaning + icon: mdi:pipe-wrench + variables: + numeric_warning_threshold: 360 + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${value.toFixed(0)} / 360 DAYS` : 'NO DATA'; + ]]] + tap_action: + action: more-info + entity: script.reset_hvac_condenser_lines_cleaned + - type: custom:button-card + template: bearstone_infra_list_row + entity: input_datetime.hvac_condenser_lines_last_cleaned + name: Last Line Cleaning + icon: mdi:calendar-check-outline + tap_action: + action: more-info + entity: script.reset_hvac_condenser_lines_cleaned diff --git a/config/dashboards/infrastructure/templates/button_card_templates.yaml b/config/dashboards/infrastructure/templates/button_card_templates.yaml index 19cbe5ca..c8bda5e7 100644 --- a/config/dashboards/infrastructure/templates/button_card_templates.yaml +++ b/config/dashboards/infrastructure/templates/button_card_templates.yaml @@ -11,6 +11,7 @@ # Notes: The compact ribbon template supports Overview status and daily controls. # Notes: Mobile templates extend the ribbon with larger accessible tap targets and text. # Notes: Named cards keep the icon beside the name; icons never consume a decorative row. +# Notes: Status KPIs support optional numeric warning checks for threshold-based summaries. ###################################################################### bearstone_infra_base: @@ -694,6 +695,20 @@ bearstone_infra_list_row: show_name: true show_state: true show_label: false + variables: + numeric_warning_threshold: '' + numeric_warning_active: > + [[[ + const thresholdRaw = variables?.numeric_warning_threshold; + const threshold = Number(thresholdRaw); + const value = Number(entity?.state); + return thresholdRaw !== undefined + && thresholdRaw !== null + && String(thresholdRaw).trim() !== '' + && Number.isFinite(threshold) + && Number.isFinite(value) + && value >= threshold; + ]]] tap_action: action: more-info styles: @@ -701,8 +716,18 @@ bearstone_infra_list_row: - border-radius: 14px - padding: 12px 12px - box-shadow: none - - border: 1px solid rgba(0,0,0,0.03) - - background: rgba(0,0,0,0.02) + - border: > + [[[ + return variables?.numeric_warning_active + ? '1px solid rgba(245,124,0,0.35)' + : '1px solid rgba(0,0,0,0.03)'; + ]]] + - background: > + [[[ + return variables?.numeric_warning_active + ? 'rgba(255,243,224,0.75)' + : 'rgba(0,0,0,0.02)'; + ]]] - box-sizing: border-box grid: - grid-template-areas: "\"i n s\"" @@ -711,7 +736,12 @@ bearstone_infra_list_row: - column-gap: 10px icon: - width: 18px - - color: var(--primary-color) + - color: > + [[[ + return variables?.numeric_warning_active + ? 'rgba(230,81,0,1)' + : 'var(--primary-color)'; + ]]] name: - font-weight: 700 - font-size: 14px @@ -725,7 +755,14 @@ bearstone_infra_list_row: - justify-self: end - font-weight: 700 - font-size: 13px - - opacity: 0.8 + - color: > + [[[ + return variables?.numeric_warning_active + ? 'rgba(230,81,0,1)' + : 'var(--primary-text-color)'; + ]]] + - opacity: > + [[[ return variables?.numeric_warning_active ? 1 : 0.8; ]]] - padding-left: 8px - white-space: nowrap @@ -935,10 +972,23 @@ bearstone_infra_status_kpi: const anyOn = (ids) => ids.some((id) => String(states[id]?.state ?? '').toLowerCase() === 'on'); const critical = anyOn(normalizeIds(variables?.critical_entity_ids)); const warning = anyOn(normalizeIds(variables?.warning_entity_ids)); + const numericWarning = (Array.isArray(variables?.numeric_warning_checks) + ? variables.numeric_warning_checks + : []).some((check) => { + const entityId = String(check?.entity_id ?? '').trim(); + const thresholdEntityId = String(check?.threshold_entity_id ?? '').trim(); + const value = Number(states[entityId]?.state); + const threshold = thresholdEntityId + ? Number(states[thresholdEntityId]?.state) + : Number(check?.threshold); + return entityId && Number.isFinite(value) && Number.isFinite(threshold) && value >= threshold; + }); const warningState = String(variables?.warning_state ?? '').toLowerCase(); const okState = String(variables?.ok_state ?? 'off').toLowerCase(); if (critical) return 'critical'; - if (warning || (warningState && raw === warningState)) return 'warning'; + if (warning || numericWarning || (warningState && raw === warningState)) return 'warning'; + const defaultSeverity = String(variables?.default_severity ?? '').toLowerCase(); + if (['healthy', 'warning', 'critical'].includes(defaultSeverity)) return defaultSeverity; return raw === okState ? 'healthy' : 'critical'; ]]] state_display: > diff --git a/config/dashboards/infrastructure/views/01_home_water.yaml b/config/dashboards/infrastructure/views/01_home_water.yaml index 33d7daa6..d398f432 100644 --- a/config/dashboards/infrastructure/views/01_home_water.yaml +++ b/config/dashboards/infrastructure/views/01_home_water.yaml @@ -3,13 +3,13 @@ # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- -# Systems View - Home Water +# Systems View - Water # Leak protection, current use, hot water, maintenance, and trends. # ------------------------------------------------------------------- -# Notes: Promoted from the approved Home Water pilot. +# Notes: Promoted from the approved Water pilot. ###################################################################### -title: Home Water +title: Water path: home-water type: sections icon: mdi:home-flood diff --git a/config/dashboards/infrastructure/views/01_hvac.yaml b/config/dashboards/infrastructure/views/01_hvac.yaml new file mode 100644 index 00000000..566e8675 --- /dev/null +++ b/config/dashboards/infrastructure/views/01_hvac.yaml @@ -0,0 +1,22 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Systems HVAC View - Filter and condenser maintenance +# Runtime, completion dates, and safe reset-script access. +# ------------------------------------------------------------------- +# Related Issue: 1817 +# Notes: Production view at `/dashboard-infrastructure/hvac`; promoted after live staging review. +###################################################################### + +title: HVAC +path: hvac +type: sections +icon: mdi:hvac +theme: default +max_columns: 4 +dense_section_placement: true +badges: [] +sections: !include /config/dashboards/infrastructure/partials/hvac_sections.yaml +cards: [] diff --git a/config/dashboards/overview/sections/00_summary.yaml b/config/dashboards/overview/sections/00_summary.yaml index bd859297..ebefc310 100644 --- a/config/dashboards/overview/sections/00_summary.yaml +++ b/config/dashboards/overview/sections/00_summary.yaml @@ -4,7 +4,7 @@ # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Overview Section - priority ribbon and daily controls -# Related Issue: 1843 +# Related Issue: 1843, 1817 # Systems first, then frequent entry points, climate, and vacuum access. # ------------------------------------------------------------------- # Notes: Equal-width pills keep the whole ribbon compact and consistent. @@ -47,6 +47,9 @@ cards: raw_suffix: ACTIVE warning_entity_ids: - binary_sensor.infra_wan_sustained_degradation + - binary_sensor.rheem_wh_telemetry_stale + - binary_sensor.docker_host_maintenance_attention + - binary_sensor.hvac_maintenance_due critical_entity_ids: - binary_sensor.infra_wan_critical - binary_sensor.infra_dns_pihole_degraded diff --git a/config/dashboards/overview/views/02_mobile.yaml b/config/dashboards/overview/views/02_mobile.yaml index ea61c8bf..7228eda5 100644 --- a/config/dashboards/overview/views/02_mobile.yaml +++ b/config/dashboards/overview/views/02_mobile.yaml @@ -201,6 +201,9 @@ sections: raw_suffix: ACTIVE warning_entity_ids: - binary_sensor.infra_wan_sustained_degradation + - binary_sensor.rheem_wh_telemetry_stale + - binary_sensor.docker_host_maintenance_attention + - binary_sensor.hvac_maintenance_due critical_entity_ids: - binary_sensor.infra_wan_critical - binary_sensor.infra_dns_pihole_degraded diff --git a/config/logbook.yaml b/config/logbook.yaml index 4d18189b..fbe62b09 100644 --- a/config/logbook.yaml +++ b/config/logbook.yaml @@ -80,6 +80,7 @@ exclude: - sensor.docker_*_disk_used_percentage - input_text.docker_*_disk_pressure_band - switch.*_container + - switch.*_container_* - "*alarm_panel_1*" - "*alarm_panel_2*" entities: @@ -106,203 +107,17 @@ exclude: - input_datetime.infra_duplicati_backup_last_success - input_text.infra_duplicati_backup_verification_status # BEGIN generated Portainer recorder/logbook noise exclusions - # Portainer per-container/status plumbing is live-only; keep aggregate Docker outage sensors historically. - - binary_sensor.docker_container_alerts_snoozed - - input_datetime.docker_container_alerts_snooze_until - - sensor.docker_monitored_container_count - - sensor.docker_monitored_switch_inventory - - sensor.docker_monitored_unavailable_count - - switch.docker_monitored_containers - - binary_sensor.carlo_hass_status - - binary_sensor.cloudflared_kch_status - - binary_sensor.cloudflared_kch_status_2 - - binary_sensor.cloudflared_wp_status - - binary_sensor.cloudflared_wp_status_2 - - binary_sensor.codex_appliance_status - - binary_sensor.codex_appliance_status_2 - - binary_sensor.codex_appliance_status_3 - - binary_sensor.codex_update_helper_d17_status - - binary_sensor.college_budget_app_status - - binary_sensor.college_budget_app_status_2 - - binary_sensor.cruise_tracker_status - - binary_sensor.cruise_tracker_status_2 - - binary_sensor.crystalsoftwashsolutions_status - - binary_sensor.dashy_status - - binary_sensor.dashy_status_2 - - binary_sensor.docker_files_codex_appliance_run_6404b6f209c6_status - - binary_sensor.docker_files_codex_appliance_run_9c89dd69eada_status - - binary_sensor.docker_files_codex_appliance_run_f05ee34fde70_status - - binary_sensor.docker_socket_proxy_status - - binary_sensor.docker_socket_proxy_status_2 - - binary_sensor.docker17_status - - binary_sensor.docker2_status - - binary_sensor.docker69_status - - binary_sensor.duplicati_status - - binary_sensor.duplicati_status_2 - - binary_sensor.esphome_status - - binary_sensor.esphome_status_2 - - binary_sensor.foodie_tracker_status - - binary_sensor.foodie_tracker_status_2 - - binary_sensor.frigate_status - - binary_sensor.frigate_status_2 - - binary_sensor.games_hub_status - - binary_sensor.games_hub_status_2 - - binary_sensor.home_assistant_status - - binary_sensor.home_assistant_status_2 - - binary_sensor.imposter_status - - binary_sensor.imposter_status_2 - - binary_sensor.kingcrafthomes_status - - binary_sensor.kingcrafthomes_status_2 - - binary_sensor.lmediaservices_status - - binary_sensor.lmediaservices_status_2 - - binary_sensor.mariadb_backup_status - - binary_sensor.mariadb_backup_status_2 - - binary_sensor.mariadb_status - - binary_sensor.mariadb_status_2 - - binary_sensor.matter_server_status - - binary_sensor.matter_server_status_2 - - binary_sensor.mqtt_status - - binary_sensor.mqtt_status_2 - - binary_sensor.nebula_sync_status + # Current retained Portainer telemetry is live-only; aggregate Docker history remains recorded. - binary_sensor.nebula_sync_status_2 - - binary_sensor.onenote_indexer_status - - binary_sensor.onenote_indexer_status_2 - - binary_sensor.paiges_bookshelf_status - - binary_sensor.panel_notes_status - - binary_sensor.panel_notes_status_2 - - binary_sensor.pihole_secondary_status - binary_sensor.pihole_secondary_status_2 - - binary_sensor.pihole_status - - binary_sensor.pihole_status_2 - - binary_sensor.poker_tracker_status - - binary_sensor.poker_tracker_status_2 - - binary_sensor.portainer_agent_status - binary_sensor.portainer_agent_status_2 - - binary_sensor.portainer_status - binary_sensor.portainer_status_2 - - binary_sensor.postgres_webhooks_backup_status - - binary_sensor.postgres_webhooks_engine_status - - binary_sensor.postgres_webhooks_engine_status_2 - - binary_sensor.rc_price_checker_status - - binary_sensor.rc_price_checker_status_2 - - binary_sensor.redis_webhooks_engine_status - - binary_sensor.redis_webhooks_engine_status_2 - - binary_sensor.rvtools_ppt_web_status - - binary_sensor.rvtools_ppt_web_status_2 - - binary_sensor.teslamate_backup_status - - binary_sensor.teslamate_database_status - - binary_sensor.teslamate_grafana_status - - binary_sensor.teslamate_status - - binary_sensor.tugtainer_agent_status - - binary_sensor.tugtainer_agent_status_2 - - binary_sensor.tugtainer_socket_proxy_status - - binary_sensor.tugtainer_socket_proxy_status_2 - - binary_sensor.tugtainer_status - - binary_sensor.tugtainer_status_2 - - binary_sensor.unifi_status - - binary_sensor.unifi_status_2 - - binary_sensor.webhooks_engine_status - - binary_sensor.webhooks_engine_status_2 - - binary_sensor.wordpress_db_backup_status - - binary_sensor.wordpress_db_status - - binary_sensor.wordpress_db_status_2 - - binary_sensor.wordpress_wp_status - - binary_sensor.wordpress_wp_status_2 - - binary_sensor.wyze_bridge_status - - binary_sensor.wyze_bridge_status_2 - - sensor.cloudflared_kch_state - - sensor.cloudflared_kch_state_2 - - sensor.cloudflared_wp_state - - sensor.cloudflared_wp_state_2 - - sensor.codex_appliance_state - - sensor.codex_appliance_state_2 - - sensor.codex_update_helper_d17_state - - sensor.codex_update_helper_d69_state - - sensor.college_budget_app_state - - sensor.college_budget_app_state_2 - - sensor.cruise_tracker_state - - sensor.cruise_tracker_state_2 - - sensor.crystalsoftwashsolutions_state - - sensor.dashy_state - - sensor.dashy_state_2 - - sensor.docker_files_codex_appliance_run_6404b6f209c6_state - - sensor.docker_files_codex_appliance_run_9c89dd69eada_state - - sensor.docker_files_codex_appliance_run_f05ee34fde70_state - - sensor.docker_socket_proxy_state - - sensor.docker_socket_proxy_state_2 - - sensor.duplicati_state - - sensor.duplicati_state_2 - - sensor.esphome_state - - sensor.esphome_state_2 - - sensor.foodie_tracker_state - - sensor.foodie_tracker_state_2 - - sensor.frigate_state - - sensor.frigate_state_2 - - sensor.games_hub_state - - sensor.games_hub_state_2 - - sensor.home_assistant_state - - sensor.home_assistant_state_2 - - sensor.imposter_state - - sensor.imposter_state_2 - - sensor.kingcrafthomes_state - - sensor.kingcrafthomes_state_2 - - sensor.lmediaservices_state - - sensor.lmediaservices_state_2 - - sensor.mariadb_backup_state - - sensor.mariadb_backup_state_2 - - sensor.mariadb_state - - sensor.mariadb_state_2 - - sensor.matter_server_state - - sensor.matter_server_state_2 - - sensor.mqtt_state - - sensor.mqtt_state_2 - - sensor.nebula_sync_state - sensor.nebula_sync_state_2 - - sensor.onenote_indexer_state - - sensor.onenote_indexer_state_2 - - sensor.paiges_bookshelf_state - - sensor.panel_notes_state - - sensor.panel_notes_state_2 - - sensor.pihole_secondary_state - - sensor.pihole_secondary_state_2 - - sensor.pihole_state - - sensor.pihole_state_2 - - sensor.poker_tracker_state - - sensor.poker_tracker_state_2 - - sensor.portainer_agent_state - - sensor.portainer_agent_state_2 - - sensor.portainer_state - - sensor.portainer_state_2 - - sensor.postgres_webhooks_backup_state - - sensor.postgres_webhooks_engine_state - - sensor.postgres_webhooks_engine_state_2 - - sensor.rc_price_checker_state - - sensor.rc_price_checker_state_2 - - sensor.redis_webhooks_engine_state - - sensor.redis_webhooks_engine_state_2 - - sensor.rvtools_ppt_web_state - - sensor.rvtools_ppt_web_state_2 - - sensor.teslamate_backup_state - - sensor.teslamate_database_state - - sensor.teslamate_grafana_state - - sensor.teslamate_state - - sensor.tugtainer_agent_state - - sensor.tugtainer_agent_state_2 - - sensor.tugtainer_socket_proxy_state - - sensor.tugtainer_socket_proxy_state_2 - - sensor.tugtainer_state - - sensor.tugtainer_state_2 - - sensor.unifi_state - - sensor.unifi_state_2 - - sensor.webhooks_engine_state - - sensor.webhooks_engine_state_2 - - sensor.wordpress_db_backup_state - - sensor.wordpress_db_state - - sensor.wordpress_db_state_2 - - sensor.wordpress_wp_state + - sensor.portainer_agent_cpu_usage_total_2 + - sensor.portainer_agent_memory_usage_percentage_2 + - sensor.portainer_cpu_usage_total_2 + - sensor.portainer_memory_usage_percentage_2 - sensor.wordpress_wp_state_2 - - sensor.wyze_bridge_state - - sensor.wyze_bridge_state_2 # END generated Portainer recorder/logbook noise exclusions - binary_sensor.onenote_indexer_job_failed - binary_sensor.onenote_indexer_index_healthy diff --git a/config/logger.yaml b/config/logger.yaml index 95fbce9f..a8a2ba82 100755 --- a/config/logger.yaml +++ b/config/logger.yaml @@ -11,6 +11,7 @@ # Notes: adjusting the severity levels for each component # Notes: Log Severities (in order of increasing severity): # Notes: notset, debug, info, warn, warning, error, fatal, critical +# Notes: Portainer image-distribution checks are update-only noise; Tugtainer owns update reporting. ###################################################################### default: info logs: @@ -56,6 +57,7 @@ logs: homeassistant.loader: warn homeassistant.components.websocket_api: error pubnub: critical + pyportainer.watcher: error py.warnings: error pyhap.hap_server: warn queueman: error diff --git a/config/packages/README.md b/config/packages/README.md index 7250aead..8b3dd93e 100755 --- a/config/packages/README.md +++ b/config/packages/README.md @@ -45,7 +45,7 @@ Kitchen Show note: `kiosk_tablet.yaml` uses Kiosk Satellite's native ESPHome ent | [![YAML source: alarm](https://img.shields.io/static/v1?label=YAML&message=alarm&color=lightgrey&logo=github&logoColor=181717)](alarm.yaml) | NodeMCU-powered perimeter monitoring with arm/disarm helpers and rich notifications. | `binary_sensor.mcu*_gpio*`, `group.family`, notify + siren scripts | | [![YAML source: alexa_media_player](https://img.shields.io/static/v1?label=YAML&message=alexa_media_player&color=lightgrey&logo=github&logoColor=181717)](alexa_media_player.yaml) | Alexa Media helper sensors including stable bedroom wake-alarm wrappers for Carlo and Stacey plus a combined next-wake view. | `sensor.last_alexa`, `sensor.bedroom_next_wake_alarm`, `sensor.bedroom_next_wake_alarm_source`, `binary_sensor.bedroom_next_wake_alarm_active` | | [![YAML source: fridge](https://img.shields.io/static/v1?label=YAML&message=fridge&color=lightgrey&logo=github&logoColor=181717)](fridge.yaml) | SmartThinQ fridge monitoring with 6-minute raw door-open alerts plus fridge/freezer status announcements. | `binary_sensor.refrigerator_door_open`, `script.notify_engine`, `script.speech_engine` | -| [![YAML source: climate](https://img.shields.io/static/v1?label=YAML&message=climate&color=lightgrey&logo=github&logoColor=181717)](climate.yaml) | Nest climate schedules plus 350-hour downstairs/450-hour upstairs/9-month AC filter reminders and 360-day condenser-line cleanout reminders with snooze and changed/cleaned actions. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/nScl2JxYSB8) | `input_datetime.*_filter_snooze_until`, `sensor.*_ac_runtime_since_last_filter_change`, `sensor.hvac_condenser_lines_days_since_cleaned`, `script.notify_engine_two_button`, mobile app action events | +| [![YAML source: climate](https://img.shields.io/static/v1?label=YAML&message=climate&color=lightgrey&logo=github&logoColor=181717)](climate.yaml) | Nest climate schedules plus 550-hour downstairs/450-hour upstairs/9-month AC filter reminders, native per-unit NFC tag change capture, per-unit change counts/cadence, and 360-day condenser-line cleanout reminders with snooze and changed/cleaned actions. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/nScl2JxYSB8) | `input_datetime.*_filter_snooze_until`, `sensor.*_ac_runtime_since_last_filter_change`, `sensor.*_ac_filter_days_since_last_change`, `sensor.*_ac_filter_change_count`, `sensor.hvac_condenser_lines_days_since_cleaned`, `script.notify_engine_two_button`, `mobile_app_notification_action`, `tag` | | [![YAML source: garadget](https://img.shields.io/static/v1?label=YAML&message=garadget&color=lightgrey&logo=github&logoColor=181717)](garadget.yaml) | MQTT-based garage door control plus arrival helpers, entry prompts, nighttime reminders, and camera context. | `cover.large_garage_door`, `cover.small_garage_door`, `group.garage_doors`, `script.open_large_garage_door_if_ready` | | [![YAML source: august](https://img.shields.io/static/v1?label=YAML&message=august&color=lightgrey&logo=github&logoColor=181717)](august.yaml) | Front-door August smart lock with Alexa Show camera pop-up when unlocked. | `lock.front_door`, media_player actions for front doorbell camera | | [![YAML source: holiday](https://img.shields.io/static/v1?label=YAML&message=holiday&color=lightgrey&logo=github&logoColor=181717)](holiday.yaml) | REST-driven US holiday + flag sensors plus the inspectable exterior lighting mode. | `sensor.holiday`, `sensor.flag`, `sensor.holiday_lighting_mode`, `sensor.holiday_lighting_scene`, JSON feed at `config/www/json_data/holidays.json` | @@ -56,14 +56,13 @@ Kitchen Show note: `kiosk_tablet.yaml` uses Kiosk Satellite's native ESPHome ent | [![YAML source: docker_infrastructure](https://img.shields.io/static/v1?label=YAML&message=docker_infrastructure&color=lightgrey&logo=github&logoColor=181717)](docker_infrastructure.yaml) | Docker host patching telemetry with persisted daily maintenance deadlines, container/stack Repairs automation, retained Portainer health telemetry, retired Portainer image-update controls, 20-minute Joanna escalation for persistent container outages including stuck `restarting`/`created` states, and weekly scheduled prune actions across docker_10/13/14/17/69. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/aceV7ObKJVA) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/using-codex-clean-up-home-assistant-entities.html) | `sensor.docker_*_apt_status`, `input_datetime.apt_docker_*_action_due`, `binary_sensor.portainer_status_2`, `sensor.portainer_cpu_usage_total_2`, `sensor.portainer_memory_usage_percentage_2`, `binary_sensor.*_stack_status`, `sensor.docker_stacks_down_count`, `repairs.create`, `repairs.remove`, `script.joanna_dispatch` | | [![YAML source: proxmox](https://img.shields.io/static/v1?label=YAML&message=proxmox&color=lightgrey&logo=github&logoColor=181717)](proxmox.yaml) | Proxmox update detection with amber dashboard maintenance state, 02:15 Joanna patch orchestration, kernel-refresh handoff hints, sequential migration and capacity gates, plus runtime and disk pressure monitoring. Docker14 recovery startup is pre-authorized only after expected VM placement and memory headroom are verified. Update availability does not create Repairs. | `binary_sensor.node_proxmox*_updates_packages`, `sensor.node_proxmox*_total_updates`, `script.joanna_dispatch`, `binary_sensor.proxmox*_runtime_healthy`, `sensor.proxmox*_disk_used_percentage` | | [![YAML source: synology_dsm](https://img.shields.io/static/v1?label=YAML&message=synology_dsm&color=lightgrey&logo=github&logoColor=181717)](synology_dsm.yaml) | Synology DSM integration health normalization for Carlo-NAS01 and Carlo-NVR, with outage-aware Joanna-first handling for lone post-outage volume warnings and Repairs escalation for persistent or non-outage problems. | `binary_sensor.carlo_*_synology_problem`, `sensor.carlo_*_synology_problem_summary`, `binary_sensor.powerwall_grid_status`, `repairs.create`, `script.joanna_dispatch` | -| [![YAML source: infrastructure](https://img.shields.io/static/v1?label=YAML&message=infrastructure&color=lightgrey&logo=github&logoColor=181717)](infrastructure.yaml) | Corroborated multi-probe WAN reachability, continuous latency/loss quality tiers, separate Ookla CLI capacity snapshots, normalized DNS/backup/domain/cert health, Nebula Sync and promoted IoT primary/backup Pi-hole consistency monitoring with Joanna dispatch, Glances-backed Docker host disk pressure with Joanna-only warning cleanup and critical Repairs, immediate website-down Repairs/Joanna dispatch plus uptime/latency SLO signals, nightly backup verification, and monthly Joanna HA log hygiene review with public-safe GitHub issue follow-up. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/F-PpsKkzcrM) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/08/home-assistant-internet-speed-monitoring-ookla.html) | `sensor.infra_wan_reachability`, `binary_sensor.infra_wan_capacity_degraded`, `binary_sensor.infra_wan_capacity_stale`, `binary_sensor.infra_wan_sustained_degradation`, `binary_sensor.infra_wan_severe_degradation`, `binary_sensor.infra_wan_critical`, `sensor.ookla_speedtest_download`, `sensor.ookla_speedtest_upload`, `sensor.infra_nebula_sync_dns_consistency`, `sensor.infra_pihole_iot_dns_consistency`, `binary_sensor.infra_nebula_sync_degraded`, `binary_sensor.infra_pihole_iot_dns_degraded`, `sensor.docker_*_disk_used_percentage`, `automation.infra_nebula_sync_health_dispatch`, `automation.infra_pihole_iot_dns_drift_dispatch`, `automation.docker_host_disk_pressure_monitor`, `automation.infra_website_down_repair_and_dispatch`, `binary_sensor.infra_website_uptime_slo_breach`, `binary_sensor.infra_website_latency_degraded`, `automation.infra_backup_nightly_verification`, `script.joanna_dispatch` | +| [![YAML source: infrastructure](https://img.shields.io/static/v1?label=YAML&message=infrastructure&color=lightgrey&logo=github&logoColor=181717)](infrastructure.yaml) | Corroborated multi-probe WAN reachability, continuous latency/loss quality tiers, separate Ookla CLI capacity snapshots, normalized DNS/backup/domain/cert health, Nebula Sync and promoted IoT primary/backup Pi-hole consistency monitoring with Joanna dispatch, Glances-backed Docker host disk pressure with Joanna-only warning cleanup and critical Repairs, quiet Bear Stone and website-down Repairs/Joanna remediation without phone alerts, uptime/latency SLO signals, nightly backup verification, and monthly Joanna HA log hygiene review with public-safe GitHub issue follow-up. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/F-PpsKkzcrM) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/08/home-assistant-internet-speed-monitoring-ookla.html) | `sensor.infra_wan_reachability`, `binary_sensor.infra_wan_capacity_degraded`, `binary_sensor.infra_wan_capacity_stale`, `binary_sensor.infra_wan_sustained_degradation`, `binary_sensor.infra_wan_severe_degradation`, `binary_sensor.infra_wan_critical`, `sensor.ookla_speedtest_download`, `sensor.ookla_speedtest_upload`, `sensor.infra_nebula_sync_dns_consistency`, `sensor.infra_pihole_iot_dns_consistency`, `binary_sensor.infra_nebula_sync_degraded`, `binary_sensor.infra_pihole_iot_dns_degraded`, `sensor.docker_*_disk_used_percentage`, `automation.infra_nebula_sync_health_dispatch`, `automation.infra_pihole_iot_dns_drift_dispatch`, `automation.docker_host_disk_pressure_monitor`, `automation.infrastructure_website_down_repair_and_dispatch`, `binary_sensor.infra_website_uptime_slo_breach`, `binary_sensor.infra_website_latency_degraded`, `automation.infra_backup_nightly_verification`, `script.joanna_dispatch` | | [![YAML source: speedtest](https://img.shields.io/static/v1?label=YAML&message=speedtest&color=lightgrey&logo=github&logoColor=181717)](speedtest.yaml) | Logs corroborated WAN outages, sustained latency/loss degradation, slow Ookla capacity advisories, and recovery without letting one throughput snapshot declare the WAN down. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/F-PpsKkzcrM) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/08/home-assistant-internet-speed-monitoring-ookla.html) | `binary_sensor.infra_wan_quality_degraded`, `binary_sensor.infra_wan_outage`, `binary_sensor.infra_wan_capacity_degraded`, `sensor.ookla_speedtest_download`, `sensor.ookla_speedtest_upload`, `script.send_to_logbook` | -| [![YAML source: uptime](https://img.shields.io/static/v1?label=YAML&message=uptime&color=lightgrey&logo=github&logoColor=181717)](uptime.yaml) | UptimeRobot-backed Bear Stone and vCloudInfo down alerts with repeating failure notices and one recovery push only after a confirmed `down` to `up` transition. API availability recovery stays silent. | `sensor.bear_stone`, `sensor.vcloudinfo_com`, `alert.*_down_alert`, `script.notify_engine` | | [![YAML source: onenote_indexer](https://img.shields.io/static/v1?label=YAML&message=onenote_indexer&color=lightgrey&logo=github&logoColor=181717)](onenote_indexer.yaml) | Dedicated-appliance OneNote indexer health/status monitoring for Joanna, explicit index-health confirmation, and failure-repair automation. Native indexer reconciliation remains the maintenance owner. | `sensor.onenote_indexer_last_job_status`, `binary_sensor.onenote_indexer_last_job_successful`, `binary_sensor.onenote_indexer_index_healthy` | | [![YAML source: mqtt_status](https://img.shields.io/static/v1?label=YAML&message=mqtt_status&color=lightgrey&logo=github&logoColor=181717)](mqtt_status.yaml) | Command-line MQTT broker reachability probe with Spook Repairs escalation and Joanna troubleshooting dispatch on outage. | `binary_sensor.mqtt_status_raw`, `binary_sensor.mqtt_broker_problem`, `repairs.create`, `rest_command.bearclaw_command` | | [![YAML source: mariadb](https://img.shields.io/static/v1?label=YAML&message=mariadb&color=lightgrey&logo=github&logoColor=181717)](mariadb.yaml) | MariaDB recorder health and capacity snapshots with hourly live metrics, weekly admin/recorder polling, and stats-ready numeric sensors. | `sensor.mariadb_status`, `sensor.database_size` | | [![YAML source: processmonitor](https://img.shields.io/static/v1?label=YAML&message=processmonitor&color=lightgrey&logo=github&logoColor=181717)](processmonitor.yaml) | Root filesystem disk-pressure monitoring with immediate digest/logbook notes at 80%, Joanna review after 10 minutes above 80%, and delayed phone alerts only if the issue stays unresolved after dispatch. | `sensor.disk_use_percent`, `repairs.create`, `script.joanna_dispatch`, `tts.clear_cache` | -| [![YAML source: tugtainer_updates](https://img.shields.io/static/v1?label=YAML&message=tugtainer_updates&color=lightgrey&logo=github&logoColor=181717)](tugtainer_updates.yaml) | Tugtainer container update notifications via webhook + persistent alerts, with immediate Joanna recovery dispatch for `### Failed:` / `### Rolled-back:` reports and every `### Available:` report. Home Assistant core participates in the 07:00 automatic rollout after a mandatory Tugtainer pre-update config check, with HTTP health and image rollback safeguards. Protected Tugtainer control-plane containers are updated one host at a time through their owning Compose stacks; duplicate Portainer image-update entities are auto-disabled because Tugtainer and Compose own rollout decisions. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/4NNOkXzUyYw) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/home-assistant-tugtainer-docker-updates.html) | `automation.tugtainer_disable_duplicate_portainer_image_updates`, `persistent_notification.create`, `event: tugtainer_available_detected`, `script.joanna_dispatch`, `input_datetime.tugtainer_last_update` | +| [![YAML source: tugtainer_updates](https://img.shields.io/static/v1?label=YAML&message=tugtainer_updates&color=lightgrey&logo=github&logoColor=181717)](tugtainer_updates.yaml) | Tugtainer container update reports via webhook, with Available-only reports processed without persistent alerts, visible `### Updated:` / `### Failed:` / `### Rolled-back:` outcomes, immediate Joanna recovery dispatch for failures, and Joanna review dispatch for every `### Available:` report. Home Assistant core participates in the 07:00 automatic rollout after a mandatory Tugtainer pre-update config check, with HTTP health and image rollback safeguards. Protected Tugtainer control-plane containers are updated one host at a time through their owning Compose stacks; duplicate Portainer image-update entities are auto-disabled because Tugtainer and Compose own rollout decisions. [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/4NNOkXzUyYw) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/home-assistant-tugtainer-docker-updates.html) | `automation.tugtainer_disable_duplicate_portainer_image_updates`, `persistent_notification.create`, `event: tugtainer_available_detected`, `script.joanna_dispatch`, `input_datetime.tugtainer_last_update` | | [![YAML source: printer](https://img.shields.io/static/v1?label=YAML&message=printer&color=lightgrey&logo=github&logoColor=181717)](printer.yaml) | Epson ink watchdog with one-day and one-week mobile snooze actions for low-ink reminders. | `input_datetime.printer_ink_snooze_until`, `sensor.epson_*`, mobile app action events | | [![YAML source: bearclaw](https://img.shields.io/static/v1?label=YAML&message=bearclaw&color=lightgrey&logo=github&logoColor=181717)](bearclaw.yaml) | Joanna/BearClaw bridge automations forward Telegram commands to codex_appliance, include LLM-first routing context for freeform text, relay replies, deliver compact adaptive TeslaMate road-trip milestone, destination-arrival, and final summaries to Carlo and Stacey, emit an active-trip vacation signal, ingest `/api/bearclaw/status` telemetry, and expose dispatch, distinct BearClaw working-memory and federated OneNote KB health, compact scheduled-job warning/error health for Systems, user-keyed 7/28/90 step trends, and personal records for Overview Health. Full Joanna reports add directional route comparisons, charging strategy, trip-aware morning context, and a private final route map. Retired QMD telemetry is not imported. Road-trip companion: [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/9-9T6v17NEw) [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/teslamate-road-trip-reports-codex.html). | `rest_command.bearclaw_*`, `event: tesla_road_trip_active`, `sensor.bearclaw_status_telemetry`, `sensor.bearclaw_scheduled_job_health`, `sensor.joanna_onenote_kb_health`, `sensor.joanna_*`, `binary_sensor.joanna_*`, `automation.bearclaw_*`, `script.notify_engine`, `script.send_to_logbook` | | [![YAML source: telegram_bot](https://img.shields.io/static/v1?label=YAML&message=telegram_bot&color=lightgrey&logo=github&logoColor=181717)](telegram_bot.yaml) | Legacy Telegram transport marker for BearClaw; the shared `joanna_send_telegram` helper now forwards through the codex_appliance direct Telegram API. | `rest_command.bearclaw_telegram_send`, `script.joanna_send_telegram` | @@ -116,7 +115,7 @@ When a package has a dedicated blog post or video, I link it right inside the YA | [![YAML source: tesla_model_y](https://img.shields.io/static/v1?label=YAML&message=tesla_model_y&color=lightgrey&logo=github&logoColor=181717)](tesla_model_y.yaml) | Garage-close timestamp used as a physical arrival signal for low-battery plug-in reminders. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/wFO0H5zgnzk) · [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/05/home-assistant-garage-automations-arrival-alerts-tesla.html) | | [![YAML source: llmvision](https://img.shields.io/static/v1?label=YAML&message=llmvision&color=lightgrey&logo=github&logoColor=181717)](llmvision.yaml) | LLM Vision package reminder flow that turns a front-door camera event into a stored package-present state and phone alert. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/nAhCezFetvI) | | [![YAML source: maintenance_log](https://img.shields.io/static/v1?label=YAML&message=maintenance_log&color=lightgrey&logo=github&logoColor=181717)](maintenance_log.yaml) + [![YAML source: 09_salt_trends](https://img.shields.io/static/v1?label=YAML&message=09_salt_trends&color=lightgrey&logo=github&logoColor=181717)](../dashboards/infrastructure/views/09_salt_trends.yaml) + [![YAML source: home_water_sections](https://img.shields.io/static/v1?label=YAML&message=home_water_sections&color=lightgrey&logo=github&logoColor=181717)](../dashboards/infrastructure/partials/home_water_sections.yaml) | Water softener salt maintenance flow with a reusable webhook, duplicate protection, helper-backed history, template sensors, and Salt Trends dashboard views. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/_OqTv9i-jAs) · [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/07/home-assistant-water-softener-salt-automation.html) · [Issue #1816](https://github.com/CCOSTAN/Home-AssistantConfig/issues/1816) | -| [![YAML source: climate](https://img.shields.io/static/v1?label=YAML&message=climate&color=lightgrey&logo=github&logoColor=181717)](climate.yaml) + [![YAML source: home_water_sections](https://img.shields.io/static/v1?label=YAML&message=home_water_sections&color=lightgrey&logo=github&logoColor=181717)](../dashboards/infrastructure/partials/home_water_sections.yaml) | HVAC blower-failure evidence, runtime-based AC filter reminders, reset actions, and 360-day condenser-line cleanout reminders. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/nScl2JxYSB8) · [Issue #273](https://github.com/CCOSTAN/Home-AssistantConfig/issues/273) | +| [![YAML source: climate](https://img.shields.io/static/v1?label=YAML&message=climate&color=lightgrey&logo=github&logoColor=181717)](climate.yaml) + [![YAML source: home_water_sections](https://img.shields.io/static/v1?label=YAML&message=home_water_sections&color=lightgrey&logo=github&logoColor=181717)](../dashboards/infrastructure/partials/home_water_sections.yaml) | HVAC blower-failure evidence, runtime-based AC filter reminders, two-unit native NFC tag change capture, per-unit change counts/cadence, reset actions, and 360-day condenser-line cleanout reminders. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/nScl2JxYSB8) · [Issue #1817](https://github.com/CCOSTAN/Home-AssistantConfig/issues/1817) · [Issue #273](https://github.com/CCOSTAN/Home-AssistantConfig/issues/273) | | [![YAML source: climate](https://img.shields.io/static/v1?label=YAML&message=climate&color=lightgrey&logo=github&logoColor=181717)](climate.yaml) + [![YAML source: printer](https://img.shields.io/static/v1?label=YAML&message=printer&color=lightgrey&logo=github&logoColor=181717)](printer.yaml) + [![YAML source: vacation_mode](https://img.shields.io/static/v1?label=YAML&message=vacation_mode&color=lightgrey&logo=github&logoColor=181717)](vacation_mode.yaml) | Snoozeable Home Assistant mobile notifications for AC filters, printer ink, and Vacation Mode missed-visit reminders. | [![Watch on YouTube](https://img.shields.io/badge/Watch-YouTube-FF0000?logo=youtube&logoColor=white)](https://youtu.be/y47KSflS1aw) · [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2026/06/home-assistant-notification-snooze-buttons.html) · [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2020/11/home-assistant-ios-actionable-notifications-example.html) | | [![YAML source: holiday](https://img.shields.io/static/v1?label=YAML&message=holiday&color=lightgrey&logo=github&logoColor=181717)](holiday.yaml) | How the holiday/flag sensor works and drives lighting playlists. | [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2019/02/breaking-down-the-flag-sensor-in-home-assistant.html) | | [![YAML source: lightning](https://img.shields.io/static/v1?label=YAML&message=lightning&color=lightgrey&logo=github&logoColor=181717)](lightning.yaml) | Blitzortung detector wiring, guarded strike alerts, one lightning speech path, and snooze workflow. | [![vCloudInfo Blog Post](https://img.shields.io/static/v1?label=vCloudInfo&message=Blog%20Post&color=21759B&logo=wordpress&logoColor=white)](https://www.vcloudinfo.com/2020/08/adding-a-lightning-sensor-to-home-assistant.html) | diff --git a/config/packages/climate.yaml b/config/packages/climate.yaml index d335d85d..8f06ef3f 100644 --- a/config/packages/climate.yaml +++ b/config/packages/climate.yaml @@ -6,9 +6,11 @@ # Climate Control - Seasonal HVAC automations and sensors. # Thermostat helpers for upstairs/downstairs comfort. # ------------------------------------------------------------------- -# Related Issue: 1571, 273 -# Notes: Filter due alerts use 350h downstairs/450h upstairs runtime thresholds, a 9-month hard limit, 3-day snooze, and Filter Changed push actions. +# Related Issue: 1571, 273, 1817 +# Notes: Filter due alerts use 550h downstairs/450h upstairs runtime thresholds, a 9-month hard limit, 3-day snooze, and Filter Changed push actions. # Notes: Condenser line cleanout uses a single 360-day reminder for both HVAC systems. +# Notes: Native NFC tag triggers call the per-unit reset scripts; a short single-run guard suppresses duplicate phone scans. +# Notes: Filter history helpers restore across restarts; reset scripts seed the prior input_datetime when history is empty. # Notes: The snooze notification pattern is also covered at https://youtu.be/y47KSflS1aw. # Video: https://youtu.be/nScl2JxYSB8 ###################################################################### @@ -83,6 +85,18 @@ template: and current > target ) }} + - name: "HVAC Maintenance Due" + unique_id: hvac_maintenance_due + device_class: problem + icon: mdi:hvac + availability: >- + {{ has_value('sensor.downstairs_ac_runtime_since_last_filter_change') + and has_value('sensor.upstairs_ac_runtime_since_last_filter_change') + and has_value('sensor.hvac_condenser_lines_days_since_cleaned') }} + state: >- + {{ states('sensor.downstairs_ac_runtime_since_last_filter_change') | float(0) >= 550 + or states('sensor.upstairs_ac_runtime_since_last_filter_change') | float(0) >= 450 + or states('sensor.hvac_condenser_lines_days_since_cleaned') | float(0) >= 360 }} sensor: - name: "Downstairs AC Cooling Numeric" @@ -107,6 +121,133 @@ template: 0 {% endif %} + - name: "Downstairs AC Filter Days Since Last Change" + unique_id: downstairs_ac_filter_days_since_last_change + unit_of_measurement: d + state_class: measurement + icon: mdi:calendar-clock + availability: >- + {% set raw = states('input_datetime.downstairs_last_filter_change') %} + {{ raw not in ['unknown', 'unavailable', 'none', ''] and as_timestamp(as_datetime(raw), default=none) is not none }} + state: >- + {% set raw = states('input_datetime.downstairs_last_filter_change') %} + {% set changed_ts = as_timestamp(as_datetime(raw), default=none) %} + {% if changed_ts is not none %} + {{ [((as_timestamp(now()) - changed_ts) / 86400) | int(0), 0] | max }} + {% else %} + 0 + {% endif %} + + - name: "Upstairs AC Filter Days Since Last Change" + unique_id: upstairs_ac_filter_days_since_last_change + unit_of_measurement: d + state_class: measurement + icon: mdi:calendar-clock + availability: >- + {% set raw = states('input_datetime.upstairs_last_filter_change') %} + {{ raw not in ['unknown', 'unavailable', 'none', ''] and as_timestamp(as_datetime(raw), default=none) is not none }} + state: >- + {% set raw = states('input_datetime.upstairs_last_filter_change') %} + {% set changed_ts = as_timestamp(as_datetime(raw), default=none) %} + {% if changed_ts is not none %} + {{ [((as_timestamp(now()) - changed_ts) / 86400) | int(0), 0] | max }} + {% else %} + 0 + {% endif %} + + - name: "Downstairs AC Filter Average Days Between Changes" + unique_id: downstairs_ac_filter_average_days_between_changes + unit_of_measurement: d + state_class: measurement + icon: mdi:calendar-range + availability: >- + {% set raw = states('input_text.downstairs_filter_change_history') %} + {{ raw not in ['unknown', 'unavailable', 'none', ''] and '||' in raw }} + state: >- + {% set raw = states('input_text.downstairs_filter_change_history') %} + {% if raw in ['unknown', 'unavailable', 'none', ''] %} + unknown + {% else %} + {% set entries = raw.split('||') %} + {% set ns = namespace(previous_ts=none, total_days=0, sample_count=0) %} + {% for entry in entries %} + {% set dt = as_datetime(entry | default('', true) | trim, default=none) %} + {% set ts = as_timestamp(dt, default=none) if dt is not none else none %} + {% if ts is not none %} + {% if ns.previous_ts is not none and ns.previous_ts > ts %} + {% set ns.total_days = ns.total_days + ((ns.previous_ts - ts) / 86400) %} + {% set ns.sample_count = ns.sample_count + 1 %} + {% endif %} + {% set ns.previous_ts = ts %} + {% endif %} + {% endfor %} + {% if ns.sample_count > 0 %} + {{ (ns.total_days / ns.sample_count) | round(1) }} + {% else %} + 0 + {% endif %} + {% endif %} + + - name: "Upstairs AC Filter Average Days Between Changes" + unique_id: upstairs_ac_filter_average_days_between_changes + unit_of_measurement: d + state_class: measurement + icon: mdi:calendar-range + availability: >- + {% set raw = states('input_text.upstairs_filter_change_history') %} + {{ raw not in ['unknown', 'unavailable', 'none', ''] and '||' in raw }} + state: >- + {% set raw = states('input_text.upstairs_filter_change_history') %} + {% if raw in ['unknown', 'unavailable', 'none', ''] %} + unknown + {% else %} + {% set entries = raw.split('||') %} + {% set ns = namespace(previous_ts=none, total_days=0, sample_count=0) %} + {% for entry in entries %} + {% set dt = as_datetime(entry | default('', true) | trim, default=none) %} + {% set ts = as_timestamp(dt, default=none) if dt is not none else none %} + {% if ts is not none %} + {% if ns.previous_ts is not none and ns.previous_ts > ts %} + {% set ns.total_days = ns.total_days + ((ns.previous_ts - ts) / 86400) %} + {% set ns.sample_count = ns.sample_count + 1 %} + {% endif %} + {% set ns.previous_ts = ts %} + {% endif %} + {% endfor %} + {% if ns.sample_count > 0 %} + {{ (ns.total_days / ns.sample_count) | round(1) }} + {% else %} + 0 + {% endif %} + {% endif %} + + - name: "Downstairs AC Filter Change Count" + unique_id: downstairs_ac_filter_change_count + unit_of_measurement: changes + state_class: total + icon: mdi:counter + state: "{{ states('counter.downstairs_filter_change_count') | int(0) }}" + + - name: "Upstairs AC Filter Change Count" + unique_id: upstairs_ac_filter_change_count + unit_of_measurement: changes + state_class: total + icon: mdi:counter + state: "{{ states('counter.upstairs_filter_change_count') | int(0) }}" + +counter: + downstairs_filter_change_count: + name: Downstairs Filter Change Count + initial: 1 + step: 1 + icon: mdi:counter + + upstairs_filter_change_count: + name: Upstairs Filter Change Count + initial: 1 + step: 1 + icon: mdi:counter + input_datetime: downstairs_last_filter_change: name: Downstairs Last Filter Change @@ -135,6 +276,17 @@ input_datetime: has_time: true initial: "2026-06-28 00:00:00" +input_text: + downstairs_filter_change_history: + name: Downstairs Filter Change History + max: 255 + icon: mdi:history + + upstairs_filter_change_history: + name: Upstairs Filter Change History + max: 255 + icon: mdi:history + # --------------------------------------------------------------------------- # Integration sensors tally runtime based on compressor state # --------------------------------------------------------------------------- @@ -165,11 +317,24 @@ script: reset_downstairs_filter: alias: Reset Downstairs Filter mode: queued + variables: + occurred_at: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + previous_change: "{{ states('input_datetime.downstairs_last_filter_change') }}" + next_history: >- + {% set current = states('input_text.downstairs_filter_change_history') | default('', true) | string %} + {% set ns = namespace(items=[occurred_at]) %} + {% for raw in current.split('||') + [previous_change] %} + {% set value = raw | trim %} + {% if value and value not in ['unknown', 'unavailable', 'none'] and value not in ns.items and (ns.items | count) < 10 %} + {% set ns.items = ns.items + [value] %} + {% endif %} + {% endfor %} + {{ ns.items | join('||') }} sequence: - service: input_datetime.set_datetime data: entity_id: input_datetime.downstairs_last_filter_change - datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + datetime: "{{ occurred_at }}" - service: input_datetime.set_datetime data: entity_id: input_datetime.downstairs_filter_snooze_until @@ -177,15 +342,36 @@ script: - service: utility_meter.reset target: entity_id: sensor.downstairs_ac_runtime_since_last_filter_change + - service: counter.increment + target: + entity_id: counter.downstairs_filter_change_count + - service: input_text.set_value + target: + entity_id: input_text.downstairs_filter_change_history + data: + value: "{{ next_history | truncate(255, true, '') }}" reset_upstairs_filter: alias: Reset Upstairs Filter mode: queued + variables: + occurred_at: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + previous_change: "{{ states('input_datetime.upstairs_last_filter_change') }}" + next_history: >- + {% set current = states('input_text.upstairs_filter_change_history') | default('', true) | string %} + {% set ns = namespace(items=[occurred_at]) %} + {% for raw in current.split('||') + [previous_change] %} + {% set value = raw | trim %} + {% if value and value not in ['unknown', 'unavailable', 'none'] and value not in ns.items and (ns.items | count) < 10 %} + {% set ns.items = ns.items + [value] %} + {% endif %} + {% endfor %} + {{ ns.items | join('||') }} sequence: - service: input_datetime.set_datetime data: entity_id: input_datetime.upstairs_last_filter_change - datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + datetime: "{{ occurred_at }}" - service: input_datetime.set_datetime data: entity_id: input_datetime.upstairs_filter_snooze_until @@ -193,6 +379,14 @@ script: - service: utility_meter.reset target: entity_id: sensor.upstairs_ac_runtime_since_last_filter_change + - service: counter.increment + target: + entity_id: counter.upstairs_filter_change_count + - service: input_text.set_value + target: + entity_id: input_text.upstairs_filter_change_history + data: + value: "{{ next_history | truncate(255, true, '') }}" reset_hvac_condenser_lines_cleaned: alias: Reset HVAC Condenser Lines Cleaned @@ -226,13 +420,47 @@ script: ### There are also some automations in the POWERWALL.yaml package when the grid is down. ############################################################################## automation: + - alias: HVAC Filter NFC Stickers + id: hvac_filter_nfc_stickers + description: Log the unit whose bound HVAC filter NFC sticker was scanned + mode: single + max_exceeded: silent + trigger: + - platform: tag + tag_id: fcf8bb30-2887-406b-8f9d-b6048e6ac196 + id: upstairs + - platform: tag + tag_id: 356ba9c3-312b-433a-975f-3f46a09ab33e + id: downstairs + action: + - choose: + - conditions: + - condition: trigger + id: upstairs + sequence: + - service: script.reset_upstairs_filter + - service: script.send_to_logbook + data: + topic: "MAINTENANCE" + message: "Upstairs HVAC filter change logged from NFC sticker." + - conditions: + - condition: trigger + id: downstairs + sequence: + - service: script.reset_downstairs_filter + - service: script.send_to_logbook + data: + topic: "MAINTENANCE" + message: "Downstairs HVAC filter change logged from NFC sticker." + - delay: "00:00:10" + - alias: Notify Climate Filter Change Due id: notify_climate_filter_change_due description: Notify when HVAC filter runtime exceeds the location threshold or age reaches 9 months trigger: - platform: numeric_state entity_id: sensor.downstairs_ac_runtime_since_last_filter_change - above: 350 + above: 550 id: downstairs_runtime - platform: time at: "09:00:00" @@ -251,7 +479,7 @@ automation: last_filter_entity: "input_datetime.{{ location }}_last_filter_change" snooze_entity: "input_datetime.{{ location }}_filter_snooze_until" runtime_hours: "{{ states(runtime_sensor) | float(0) }}" - runtime_threshold_hours: "{{ 450 if location == 'upstairs' else 350 }}" + runtime_threshold_hours: "{{ 450 if location == 'upstairs' else 550 }}" last_filter_ts: "{{ as_timestamp(states(last_filter_entity), 0) }}" filter_age_days: "{{ ((as_timestamp(now()) - (last_filter_ts | float(0))) / 86400) | round(0) }}" due_reason: >- diff --git a/config/packages/docker_infrastructure.yaml b/config/packages/docker_infrastructure.yaml index ebae2b98..e12fd980 100644 --- a/config/packages/docker_infrastructure.yaml +++ b/config/packages/docker_infrastructure.yaml @@ -24,11 +24,13 @@ # Notes: Tapple is now served by `games_hub` on `/tapple/`; do not keep a standalone `tapple` container switch in the monitored group. # Notes: Teslamate and crystalsoftwashsolutions are live services and should remain in the monitored group when their discovery switches are present. # Notes: Treat telemetry reconnects from unavailable/unknown to a concrete stopped state as actionable outages. +# Notes: An empty discovered container inventory is telemetry degradation, not a healthy zero-container state. +# Notes: Host APT maintenance rolls up independently from container telemetry so pending or stale host reports cannot remain buried on the Docker view. # Notes: Treat stuck `restarting` and `created` states as down so monitored containers dispatch remediation. # Notes: Infra Info was removed; BearClaw Admin is the planning snapshot surface. -# Notes: codex_appliance moved to a dedicated VM; keep the standard codex_appliance switches and retire the legacy hashed discovery entity when it disappears. +# Notes: codex_appliance moved to a dedicated VM; retain only its current Portainer discovery switch. # Notes: Paige's Bookshelf is a live monitored service and should remain in the group when its discovery switch is present. -# Notes: Panel Notes currently exposes both `switch.panel_notes_container` and `switch.panel_notes_container_2`; keep the discovered live switches aligned until the duplicate is removed. +# Notes: Panel Notes and OneNote Indexer use their current `_2` discovery switches after stale registry cleanup. # Notes: Retired repair cleanup clears stale legacy codex_appliance and Dozzle repair IDs. ###################################################################### @@ -120,34 +122,20 @@ switch: name: Docker Monitored Containers unique_id: docker_monitored_containers entities: - - switch.cloudflared_kch_container - switch.cloudflared_kch_container_2 - - switch.cloudflared_wp_container - switch.cloudflared_wp_container_2 - - switch.college_budget_app_container - switch.college_budget_app_container_2 - - switch.cruise_tracker_container - switch.cruise_tracker_container_2 - switch.crystalsoftwashsolutions_container - - switch.dashy_container - switch.dashy_container_2 - - switch.docker_socket_proxy_container - switch.docker_socket_proxy_container_2 - - switch.codex_appliance_container - switch.codex_appliance_container_2 - - switch.duplicati_container - switch.duplicati_container_2 - - switch.esphome_container - switch.esphome_container_2 - - switch.foodie_tracker_container - switch.foodie_tracker_container_2 - - switch.frigate_container - switch.frigate_container_2 - - switch.games_hub_container - switch.games_hub_container_2 - - switch.home_assistant_container - switch.home_assistant_container_2 - - switch.imposter_container - switch.imposter_container_2 - switch.justin_internship_crm_container - switch.justin_internship_crm_enricher_container @@ -155,65 +143,39 @@ switch: - switch.jobs_backend_container - switch.jobs_db_container - switch.jobs_frontend_container - - switch.kingcrafthomes_container - switch.kingcrafthomes_container_2 - - switch.lmediaservices_container - switch.lmediaservices_container_2 - - switch.mariadb_backup_container - switch.mariadb_backup_container_2 - - switch.mariadb_container - switch.mariadb_container_2 - - switch.matter_server_container - switch.matter_server_container_2 - - switch.mqtt_container - switch.mqtt_container_2 - - switch.nebula_sync_container - switch.nebula_sync_container_2 - - switch.onenote_indexer_container - switch.onenote_indexer_container_2 - switch.onenote_indexer_stacey_container - switch.paiges_bookshelf_container - - switch.panel_notes_container - switch.panel_notes_container_2 - - switch.pihole_container - switch.pihole_container_2 - - switch.pihole_secondary_container - switch.pihole_secondary_container_2 - - switch.poker_tracker_container - switch.poker_tracker_container_2 - - switch.portainer_agent_container - switch.portainer_agent_container_2 - - switch.portainer_container - switch.portainer_container_2 - switch.postgres_webhooks_backup_container - - switch.postgres_webhooks_engine_container - switch.postgres_webhooks_engine_container_2 - - switch.rc_price_checker_container - switch.rc_price_checker_container_2 - - switch.redis_webhooks_engine_container - switch.redis_webhooks_engine_container_2 - - switch.rvtools_ppt_web_container - switch.rvtools_ppt_web_container_2 - switch.teslamate_backup_container - switch.teslamate_container - switch.teslamate_database_container - switch.teslamate_grafana_container - - switch.tugtainer_agent_container - switch.tugtainer_agent_container_2 - - switch.tugtainer_container - switch.tugtainer_container_2 - - switch.tugtainer_socket_proxy_container - switch.tugtainer_socket_proxy_container_2 - - switch.unifi_container - switch.unifi_container_2 - - switch.webhooks_engine_container - switch.webhooks_engine_container_2 - switch.wordpress_db_backup_container - - switch.wordpress_db_container - switch.wordpress_db_container_2 - - switch.wordpress_wp_container - switch.wordpress_wp_container_2 - - switch.wyze_bridge_container - switch.wyze_bridge_container_2 @@ -620,6 +582,29 @@ template: {% endif %} - binary_sensor: + - name: "Docker Host Maintenance Attention" + unique_id: docker_host_maintenance_attention + device_class: problem + icon: mdi:package-up + state: >- + {% set entities = [ + 'sensor.docker_10_apt_status', + 'sensor.docker_13_apt_status', + 'sensor.docker_14_apt_status', + 'sensor.docker_17_apt_status', + 'sensor.docker_69_apt_status' + ] %} + {% set ns = namespace(attention=false) %} + {% for entity_id in entities %} + {% set status = states(entity_id) | upper %} + {% if status in ['UNKNOWN', 'UNAVAILABLE', 'NONE', ''] or + 'PENDING' in status or 'REBOOT REQ' in status or + status.startswith('ERROR') %} + {% set ns.attention = true %} + {% endif %} + {% endfor %} + {{ ns.attention }} + - name: "Docker Container Telemetry Degraded" unique_id: docker_container_telemetry_degraded device_class: problem @@ -628,7 +613,7 @@ template: {% set total = states('sensor.docker_monitored_container_count') | int(0) %} {% set unavailable = states('sensor.docker_monitored_unavailable_count') | int(0) %} {% set threshold = [3, ((total * 0.6) | round(0, 'ceil') | int(0))] | max %} - {{ total > 0 and unavailable >= threshold }} + {{ total == 0 or unavailable >= threshold }} - name: "Docker Container Alerts Snoozed" unique_id: docker_container_alerts_snoozed diff --git a/config/packages/infrastructure.yaml b/config/packages/infrastructure.yaml index 1a761a56..77a9b40f 100644 --- a/config/packages/infrastructure.yaml +++ b/config/packages/infrastructure.yaml @@ -27,8 +27,10 @@ # Notes: Promoted IoT DNS consistency compares primary/backup Pi-hole answers for reserved IoT host records. # Notes: Garage compute health uses the same 145 F threshold as the existing temperature notification. # Notes: Immediate website-down states create Repairs; the installed UptimeRobot integration exposes current state, not trend rollups. +# Notes: Bear Stone and website outages dispatch Joanna quietly; no direct phone alerts are sent. # Notes: Dashboard aggregate issue count is live-only; its underlying degraded sensors retain actionable history. # Notes: Dashboard aggregate issue count includes compact BearClaw scheduled-job warning/error health. +# Notes: Docker host maintenance is counted separately from container runtime health so pending packages and stale host reports surface on Systems. # Notes: Home Water rollup includes Rheem faults/telemetry loss plus active Phyn alerts and valve closure. ###################################################################### @@ -188,6 +190,7 @@ template: icon: mdi:counter state: >- {% set ids = [ + 'binary_sensor.bear_stone', 'binary_sensor.vcloudinfo_com', 'binary_sensor.ipmer_com', 'binary_sensor.fordst_com', @@ -206,6 +209,7 @@ template: attributes: monitored_entities: >- {{ [ + 'binary_sensor.bear_stone', 'binary_sensor.vcloudinfo_com', 'binary_sensor.ipmer_com', 'binary_sensor.fordst_com', @@ -213,6 +217,7 @@ template: ] }} down_entities: >- {% set ids = [ + 'binary_sensor.bear_stone', 'binary_sensor.vcloudinfo_com', 'binary_sensor.ipmer_com', 'binary_sensor.fordst_com', @@ -239,6 +244,8 @@ template: 'binary_sensor.infra_compute_degraded', 'binary_sensor.infra_storage_degraded', 'binary_sensor.infra_external_monitoring_degraded', + 'binary_sensor.docker_host_maintenance_attention', + 'binary_sensor.hvac_maintenance_due', 'binary_sensor.home_water_problem', 'binary_sensor.kiosk_devices_need_attention' ] %} @@ -253,6 +260,8 @@ template: 'binary_sensor.infra_compute_degraded', 'binary_sensor.infra_storage_degraded', 'binary_sensor.infra_external_monitoring_degraded', + 'binary_sensor.docker_host_maintenance_attention', + 'binary_sensor.hvac_maintenance_due', 'binary_sensor.home_water_problem', 'binary_sensor.kiosk_devices_need_attention' ] %} @@ -423,19 +432,13 @@ template: state: >- {% set dns_state = states('sensor.infra_nebula_sync_dns_consistency') | lower %} {% set portainer_known = [ - expand('binary_sensor.nebula_sync_status') | count > 0, expand('binary_sensor.nebula_sync_status_2') | count > 0, - expand('sensor.nebula_sync_state') | count > 0, expand('sensor.nebula_sync_state_2') | count > 0, - expand('switch.nebula_sync_container') | count > 0, expand('switch.nebula_sync_container_2') | count > 0 ] | select('equalto', true) | list | count > 0 %} {% set portainer_ok = [ - is_state('binary_sensor.nebula_sync_status', 'on'), is_state('binary_sensor.nebula_sync_status_2', 'on'), - (states('sensor.nebula_sync_state') | lower) == 'running', (states('sensor.nebula_sync_state_2') | lower) == 'running', - is_state('switch.nebula_sync_container', 'on'), is_state('switch.nebula_sync_container_2', 'on') ] | select('equalto', true) | list | count > 0 %} {{ dns_state != 'ok' or (portainer_known and not portainer_ok) }} @@ -449,12 +452,9 @@ template: secondary_fqdn: "{{ state_attr('sensor.infra_nebula_sync_dns_consistency', 'secondary_fqdn') }}" primary_reverse: "{{ state_attr('sensor.infra_nebula_sync_dns_consistency', 'primary_reverse') }}" secondary_reverse: "{{ state_attr('sensor.infra_nebula_sync_dns_consistency', 'secondary_reverse') }}" - nebula_status: "{{ states('binary_sensor.nebula_sync_status') }}" - nebula_status_2: "{{ states('binary_sensor.nebula_sync_status_2') }}" - nebula_state: "{{ states('sensor.nebula_sync_state') }}" - nebula_state_2: "{{ states('sensor.nebula_sync_state_2') }}" - pihole_secondary_status: "{{ states('binary_sensor.pihole_secondary_status') }}" - pihole_secondary_status_2: "{{ states('binary_sensor.pihole_secondary_status_2') }}" + nebula_status: "{{ states('binary_sensor.nebula_sync_status_2') }}" + nebula_state: "{{ states('sensor.nebula_sync_state_2') }}" + pihole_secondary_status: "{{ states('binary_sensor.pihole_secondary_status_2') }}" - name: "Infra Pihole IoT DNS Degraded" unique_id: infra_pihole_iot_dns_degraded @@ -617,12 +617,16 @@ automation: entity_ids: - binary_sensor.infra_website_degraded - sensor.infra_website_down_count + - binary_sensor.bear_stone + - sensor.bear_stone - binary_sensor.vcloudinfo_com - sensor.vcloudinfo_com - sensor.wordpress_wp_state_2 - switch.wordpress_wp_container_2 diagnostics: >- down_entities={{ down_summary }}; + bear_stone_sensor={{ states('sensor.bear_stone') }}; + bear_stone_binary={{ states('binary_sensor.bear_stone') }}; vcloudinfo_sensor={{ states('sensor.vcloudinfo_com') }}; vcloudinfo_binary={{ states('binary_sensor.vcloudinfo_com') }}; wordpress_state={{ states('sensor.wordpress_wp_state_2') }}; @@ -630,6 +634,8 @@ automation: cloudflared_wp={{ states('switch.cloudflared_wp_container_2') }}. request: >- Investigate and resolve the monitored website outage. For + Bear Stone, start with public HTTPS reachability and the + Home Assistant/Cloudflare path. For vcloudinfo.com, start with public HTTPS reachability, wordpress_wp, wordpress_db, and cloudflared_wp telemetry. Verify public HTTP 200 recovery before closing out. Do not @@ -726,12 +732,9 @@ automation: dns_state: "{{ states('sensor.infra_nebula_sync_dns_consistency') }}" previous_band: "{{ states('input_text.infra_nebula_sync_health_band') | lower }}" degraded: "{{ is_state('binary_sensor.infra_nebula_sync_degraded', 'on') }}" - nebula_status: "{{ states('binary_sensor.nebula_sync_status') }}" - nebula_status_alt: "{{ states('binary_sensor.nebula_sync_status_2') }}" - nebula_state: "{{ states('sensor.nebula_sync_state') }}" - nebula_state_alt: "{{ states('sensor.nebula_sync_state_2') }}" - pihole_secondary_status: "{{ states('binary_sensor.pihole_secondary_status') }}" - pihole_secondary_status_alt: "{{ states('binary_sensor.pihole_secondary_status_2') }}" + nebula_status: "{{ states('binary_sensor.nebula_sync_status_2') }}" + nebula_state: "{{ states('sensor.nebula_sync_state_2') }}" + pihole_secondary_status: "{{ states('binary_sensor.pihole_secondary_status_2') }}" action: - choose: - conditions: "{{ degraded and previous_band != 'warning' }}" @@ -748,11 +751,8 @@ automation: entity_ids: - sensor.infra_nebula_sync_dns_consistency - binary_sensor.infra_nebula_sync_degraded - - binary_sensor.nebula_sync_status - binary_sensor.nebula_sync_status_2 - - sensor.nebula_sync_state - sensor.nebula_sync_state_2 - - binary_sensor.pihole_secondary_status - binary_sensor.pihole_secondary_status_2 diagnostics: >- issue_id={{ issue_id }}, @@ -766,11 +766,8 @@ automation: primary_reverse={{ state_attr('sensor.infra_nebula_sync_dns_consistency', 'primary_reverse') }}, secondary_reverse={{ state_attr('sensor.infra_nebula_sync_dns_consistency', 'secondary_reverse') }}, nebula_status={{ nebula_status }}, - nebula_status_2={{ nebula_status_alt }}, nebula_state={{ nebula_state }}, - nebula_state_2={{ nebula_state_alt }}, pihole_secondary_status={{ pihole_secondary_status }}, - pihole_secondary_status_2={{ pihole_secondary_status_alt }}, primary_dns=192.168.10.10, backup_dns=192.168.10.14 request: >- diff --git a/config/packages/maintenance_log.yaml b/config/packages/maintenance_log.yaml index d0087ebe..f148962e 100644 --- a/config/packages/maintenance_log.yaml +++ b/config/packages/maintenance_log.yaml @@ -4,11 +4,13 @@ # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Maintenance Logging - webhook ingest + graphable HA history +# Related Issue: 1816 # Stores water softener salt maintenance events in recorder-backed helpers. # ------------------------------------------------------------------- # Video: https://youtu.be/_OqTv9i-jAs # Blog: https://www.vcloudinfo.com/2026/07/home-assistant-water-softener-salt-automation.html -# Notes: Webhook id is bearclaw_maintenance_log_v1; payloads can come from agents, NFC tags, QR codes, or dashboard buttons. +# Notes: Webhook id is bearclaw_maintenance_log_v1; internal maintenance_log events reuse the same queued processor. +# Notes: The water-softener NFC tag uses a short single-run guard to suppress duplicate phone scans. # Notes: Duplicate event_id values are ignored to prevent double-count totals. # Notes: Recent event history string format is "YYYY-MM-DD HH:MM:SS|amount|note||...". # Notes: Long-term stats live on template sensors; helpers are internal state for restore/calculation. @@ -147,21 +149,58 @@ template: state: "{{ states('input_number.water_softener_salt_total_added_lb') | float(0) | round(1) }}" automation: + - alias: "Maintenance Log - Water Softener Salt NFC" + id: water_softener_salt_nfc_refill + description: Log one 80 lb salt refill when the bound water-softener NFC sticker is scanned + mode: single + max_exceeded: silent + + trigger: + - platform: tag + tag_id: 21d5898a49e64903911f1dbaac931cb4 + + action: + - event: maintenance_log + event_data: + item_key: water_softener_salt + action: add + amount_value: 80 + amount_unit: lb + event_id: "nfc_water_softener_salt_{{ trigger.event.context.id }}" + occurred_at: "{{ now().isoformat() }}" + actor: home_assistant_nfc + raw_text: "Water softener salt refill NFC sticker scanned." + parse_confidence: deterministic + + - delay: "00:00:10" + - alias: "Maintenance Log - Joanna Webhook Ingest" id: 1c9fba4f-fef5-4da9-82d4-4049deff17cf + description: Process webhook and internal maintenance events through one deduplicated recorder-backed path mode: queued max: 30 trigger: - platform: webhook + id: webhook webhook_id: bearclaw_maintenance_log_v1 allowed_methods: - POST - PUT local_only: false + - platform: event + id: internal_event + event_type: maintenance_log variables: - payload: "{{ trigger.json if trigger.json is mapping else dict() }}" + payload: >- + {% if trigger.id == 'webhook' and trigger.json is mapping %} + {{ trigger.json }} + {% elif trigger.id == 'internal_event' and trigger.event.data is mapping %} + {{ trigger.event.data }} + {% else %} + {{ dict() }} + {% endif %} source_item: "{{ payload.item_key | default('', true) | string | trim | lower }}" item_key: >- {% set aliases = { diff --git a/config/packages/tugtainer_updates.yaml b/config/packages/tugtainer_updates.yaml index 51672654..7c60f9db 100644 --- a/config/packages/tugtainer_updates.yaml +++ b/config/packages/tugtainer_updates.yaml @@ -5,10 +5,10 @@ # ------------------------------------------------------------------- # Tugtainer Updates - Container update notifications via webhook # Related Issue: 1561 -# Receives Apprise JSON payloads from Tugtainer and posts HA alerts. +# Receives Apprise JSON payloads from Tugtainer and posts outcome alerts. # ------------------------------------------------------------------- # Notes: Expects JSON with title/message/type from the Tugtainer template. -# Notes: Creates persistent notifications and stamps last-update time. +# Notes: Available-only reports stay silent in HA; Updated, Failed, and Rolled-back reports remain visible. # Notes: Fires update events independently when a report has both Available and Updated sections. # Notes: Fires `tugtainer_home_assistant_core_updated` when `### Updated:` includes Home Assistant. # Notes: Every Available report dispatches Joanna; Tugtainer owns report scheduling and de-duplication. @@ -78,7 +78,7 @@ automation: - alias: "Tugtainer Update Report" id: tugtainer_update_report - description: "Receive Tugtainer reports, post persistent alerts, and dispatch failed updates." + description: "Receive Tugtainer reports, suppress Available-only alerts, and dispatch update work." mode: queued trigger: - platform: webhook @@ -99,6 +99,8 @@ automation: {% set sections = message | regex_findall('(?is)###\\s*updated:\\s*(.*?)(?:\\n###\\s|$)') %} {{ sections[0] if sections | count > 0 else '' }} has_updated_section: "{{ updated_section | trim != '' }}" + show_persistent_notification: >- + {{ not has_available_section or has_updated_section or has_failure_section }} ha_updated_line: >- {% set matches = updated_section | regex_findall('(?im)^.*(?:home-assistant|ghcr\\.io/home-assistant/home-assistant).*$') %} {{ matches[0] if matches | count > 0 else '' }} @@ -140,10 +142,14 @@ automation: entity_id: input_datetime.tugtainer_last_update data: datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" - - service: persistent_notification.create - data: - title: "{{ title }}" - message: "{{ full_message }}" + - if: + - condition: template + value_template: "{{ show_persistent_notification }}" + then: + - service: persistent_notification.create + data: + title: "{{ title }}" + message: "{{ full_message }}" - if: - condition: template value_template: "{{ has_available_section }}" diff --git a/config/recorder.yaml b/config/recorder.yaml index ac0547be..9a71949a 100755 --- a/config/recorder.yaml +++ b/config/recorder.yaml @@ -75,6 +75,7 @@ exclude: - binary_sensor.carlo_maxpro_* - binary_sensor.stacey_iphone11_* - switch.*_container + - switch.*_container_* - sensor.*_stack_type - sensor.*_stack_containers_count - binary_sensor.*_stack_status @@ -213,203 +214,17 @@ exclude: - timer.kitchen_kiosk_detail_display - timer.kitchen_kiosk_radar_display # BEGIN generated Portainer recorder/logbook noise exclusions - # Portainer per-container/status plumbing is live-only; keep aggregate Docker outage sensors historically. - - binary_sensor.docker_container_alerts_snoozed - - input_datetime.docker_container_alerts_snooze_until - - sensor.docker_monitored_container_count - - sensor.docker_monitored_switch_inventory - - sensor.docker_monitored_unavailable_count - - switch.docker_monitored_containers - - binary_sensor.carlo_hass_status - - binary_sensor.cloudflared_kch_status - - binary_sensor.cloudflared_kch_status_2 - - binary_sensor.cloudflared_wp_status - - binary_sensor.cloudflared_wp_status_2 - - binary_sensor.codex_appliance_status - - binary_sensor.codex_appliance_status_2 - - binary_sensor.codex_appliance_status_3 - - binary_sensor.codex_update_helper_d17_status - - binary_sensor.college_budget_app_status - - binary_sensor.college_budget_app_status_2 - - binary_sensor.cruise_tracker_status - - binary_sensor.cruise_tracker_status_2 - - binary_sensor.crystalsoftwashsolutions_status - - binary_sensor.dashy_status - - binary_sensor.dashy_status_2 - - binary_sensor.docker_files_codex_appliance_run_6404b6f209c6_status - - binary_sensor.docker_files_codex_appliance_run_9c89dd69eada_status - - binary_sensor.docker_files_codex_appliance_run_f05ee34fde70_status - - binary_sensor.docker_socket_proxy_status - - binary_sensor.docker_socket_proxy_status_2 - - binary_sensor.docker17_status - - binary_sensor.docker2_status - - binary_sensor.docker69_status - - binary_sensor.duplicati_status - - binary_sensor.duplicati_status_2 - - binary_sensor.esphome_status - - binary_sensor.esphome_status_2 - - binary_sensor.foodie_tracker_status - - binary_sensor.foodie_tracker_status_2 - - binary_sensor.frigate_status - - binary_sensor.frigate_status_2 - - binary_sensor.games_hub_status - - binary_sensor.games_hub_status_2 - - binary_sensor.home_assistant_status - - binary_sensor.home_assistant_status_2 - - binary_sensor.imposter_status - - binary_sensor.imposter_status_2 - - binary_sensor.kingcrafthomes_status - - binary_sensor.kingcrafthomes_status_2 - - binary_sensor.lmediaservices_status - - binary_sensor.lmediaservices_status_2 - - binary_sensor.mariadb_backup_status - - binary_sensor.mariadb_backup_status_2 - - binary_sensor.mariadb_status - - binary_sensor.mariadb_status_2 - - binary_sensor.matter_server_status - - binary_sensor.matter_server_status_2 - - binary_sensor.mqtt_status - - binary_sensor.mqtt_status_2 - - binary_sensor.nebula_sync_status + # Current retained Portainer telemetry is live-only; aggregate Docker history remains recorded. - binary_sensor.nebula_sync_status_2 - - binary_sensor.onenote_indexer_status - - binary_sensor.onenote_indexer_status_2 - - binary_sensor.paiges_bookshelf_status - - binary_sensor.panel_notes_status - - binary_sensor.panel_notes_status_2 - - binary_sensor.pihole_secondary_status - binary_sensor.pihole_secondary_status_2 - - binary_sensor.pihole_status - - binary_sensor.pihole_status_2 - - binary_sensor.poker_tracker_status - - binary_sensor.poker_tracker_status_2 - - binary_sensor.portainer_agent_status - binary_sensor.portainer_agent_status_2 - - binary_sensor.portainer_status - binary_sensor.portainer_status_2 - - binary_sensor.postgres_webhooks_backup_status - - binary_sensor.postgres_webhooks_engine_status - - binary_sensor.postgres_webhooks_engine_status_2 - - binary_sensor.rc_price_checker_status - - binary_sensor.rc_price_checker_status_2 - - binary_sensor.redis_webhooks_engine_status - - binary_sensor.redis_webhooks_engine_status_2 - - binary_sensor.rvtools_ppt_web_status - - binary_sensor.rvtools_ppt_web_status_2 - - binary_sensor.teslamate_backup_status - - binary_sensor.teslamate_database_status - - binary_sensor.teslamate_grafana_status - - binary_sensor.teslamate_status - - binary_sensor.tugtainer_agent_status - - binary_sensor.tugtainer_agent_status_2 - - binary_sensor.tugtainer_socket_proxy_status - - binary_sensor.tugtainer_socket_proxy_status_2 - - binary_sensor.tugtainer_status - - binary_sensor.tugtainer_status_2 - - binary_sensor.unifi_status - - binary_sensor.unifi_status_2 - - binary_sensor.webhooks_engine_status - - binary_sensor.webhooks_engine_status_2 - - binary_sensor.wordpress_db_backup_status - - binary_sensor.wordpress_db_status - - binary_sensor.wordpress_db_status_2 - - binary_sensor.wordpress_wp_status - - binary_sensor.wordpress_wp_status_2 - - binary_sensor.wyze_bridge_status - - binary_sensor.wyze_bridge_status_2 - - sensor.cloudflared_kch_state - - sensor.cloudflared_kch_state_2 - - sensor.cloudflared_wp_state - - sensor.cloudflared_wp_state_2 - - sensor.codex_appliance_state - - sensor.codex_appliance_state_2 - - sensor.codex_update_helper_d17_state - - sensor.codex_update_helper_d69_state - - sensor.college_budget_app_state - - sensor.college_budget_app_state_2 - - sensor.cruise_tracker_state - - sensor.cruise_tracker_state_2 - - sensor.crystalsoftwashsolutions_state - - sensor.dashy_state - - sensor.dashy_state_2 - - sensor.docker_files_codex_appliance_run_6404b6f209c6_state - - sensor.docker_files_codex_appliance_run_9c89dd69eada_state - - sensor.docker_files_codex_appliance_run_f05ee34fde70_state - - sensor.docker_socket_proxy_state - - sensor.docker_socket_proxy_state_2 - - sensor.duplicati_state - - sensor.duplicati_state_2 - - sensor.esphome_state - - sensor.esphome_state_2 - - sensor.foodie_tracker_state - - sensor.foodie_tracker_state_2 - - sensor.frigate_state - - sensor.frigate_state_2 - - sensor.games_hub_state - - sensor.games_hub_state_2 - - sensor.home_assistant_state - - sensor.home_assistant_state_2 - - sensor.imposter_state - - sensor.imposter_state_2 - - sensor.kingcrafthomes_state - - sensor.kingcrafthomes_state_2 - - sensor.lmediaservices_state - - sensor.lmediaservices_state_2 - - sensor.mariadb_backup_state - - sensor.mariadb_backup_state_2 - - sensor.mariadb_state - - sensor.mariadb_state_2 - - sensor.matter_server_state - - sensor.matter_server_state_2 - - sensor.mqtt_state - - sensor.mqtt_state_2 - - sensor.nebula_sync_state - sensor.nebula_sync_state_2 - - sensor.onenote_indexer_state - - sensor.onenote_indexer_state_2 - - sensor.paiges_bookshelf_state - - sensor.panel_notes_state - - sensor.panel_notes_state_2 - - sensor.pihole_secondary_state - - sensor.pihole_secondary_state_2 - - sensor.pihole_state - - sensor.pihole_state_2 - - sensor.poker_tracker_state - - sensor.poker_tracker_state_2 - - sensor.portainer_agent_state - - sensor.portainer_agent_state_2 - - sensor.portainer_state - - sensor.portainer_state_2 - - sensor.postgres_webhooks_backup_state - - sensor.postgres_webhooks_engine_state - - sensor.postgres_webhooks_engine_state_2 - - sensor.rc_price_checker_state - - sensor.rc_price_checker_state_2 - - sensor.redis_webhooks_engine_state - - sensor.redis_webhooks_engine_state_2 - - sensor.rvtools_ppt_web_state - - sensor.rvtools_ppt_web_state_2 - - sensor.teslamate_backup_state - - sensor.teslamate_database_state - - sensor.teslamate_grafana_state - - sensor.teslamate_state - - sensor.tugtainer_agent_state - - sensor.tugtainer_agent_state_2 - - sensor.tugtainer_socket_proxy_state - - sensor.tugtainer_socket_proxy_state_2 - - sensor.tugtainer_state - - sensor.tugtainer_state_2 - - sensor.unifi_state - - sensor.unifi_state_2 - - sensor.webhooks_engine_state - - sensor.webhooks_engine_state_2 - - sensor.wordpress_db_backup_state - - sensor.wordpress_db_state - - sensor.wordpress_db_state_2 - - sensor.wordpress_wp_state + - sensor.portainer_agent_cpu_usage_total_2 + - sensor.portainer_agent_memory_usage_percentage_2 + - sensor.portainer_cpu_usage_total_2 + - sensor.portainer_memory_usage_percentage_2 - sensor.wordpress_wp_state_2 - - sensor.wyze_bridge_state - - sensor.wyze_bridge_state_2 # END generated Portainer recorder/logbook noise exclusions - binary_sensor.bear_stone - binary_sensor.onenote_indexer_job_failed