Enhance Home Assistant configuration with updates to README and process monitoring scripts

- Updated README.md in config/ to reflect changes in scripts and scenes, including AGENT engineer handoff details.
- Revised README.md in packages/ to clarify package functionalities and added new Synology DSM monitoring package.
- Improved processmonitor.yaml to include Joanna dispatch for disk usage alerts and recovery actions.
- Introduced synology_dsm.yaml for monitoring Synology DSM health, including integration problems and automated repair dispatch.
- Enhanced script documentation to include AGENT engineer handoff processes for better clarity and usage.
pull/1719/head
Carlo Costanzo 1 month ago
parent 202d0fcd4b
commit 5010c734b6

@ -34,7 +34,7 @@ Live view of the `config/` directory my production Home Assistant instance loads
- **Packages (`packages/`)** complete subsystems that bundle sensors, switches, automations, scripts, and lovelace assets for a single feature (alarm, garage, water shutoff, etc.).
- **Container updates** `packages/tugtainer_updates.yaml` logs container updates from Tugtainer into Home Assistant persistent notifications.
- **Automations (`automation/`)** event-driven YAML broken out by area or device; the legacy `automations.yaml` remains for UI-created flows.
- **Scripts & scenes (`script/`, `scene/`)** curated lighting and ambiance logic used by presence, holiday, and seasonal routines.
- **Scripts & scenes (`script/`, `scene/`)** curated lighting, notification, and AGENT engineer handoff helpers used by presence, holiday, seasonal, and infrastructure routines.
- **Templates (`templates/`)** Jinja helpers and speech templates reused by the notify/speech engines.
- **Dashboards (`dashboards/`)** YAML-managed Lovelace dashboards and UI resources (generated from storage, then maintained as code).
- **www/ + custom components** branding assets, floorplans, and any custom components the core install depends on.
@ -44,7 +44,7 @@ Live view of the `config/` directory my production Home Assistant instance loads
| --- | --- | --- |
| Packages | Self-contained subsystems that highlight patterns like combined alerts + actions. | [packages/alarm.yaml](packages/alarm.yaml), [packages/garadget.yaml](packages/garadget.yaml), [packages/powerwall.yaml](packages/powerwall.yaml) |
| Automations | Real-world triggers that tie Zwave, MQTT, and REST sensors into the rest of the house. | [automation/garage_entry_light.yaml](automation/garage_entry_light.yaml), [automation/dark_rainy_day.yaml](automation/dark_rainy_day.yaml), [automation/dash_buttons.yaml](automation/dash_buttons.yaml) |
| Scripts | Reusable building blocks for lighting, notifications, and safety responses. | [script/monthly_color_scene.yaml](script/monthly_color_scene.yaml), [script/notify_engine.yaml](script/notify_engine.yaml), [script/speech_engine.yaml](script/speech_engine.yaml) |
| Scripts | Reusable building blocks for lighting, notifications, safety responses, and Joanna/BearClaw remediation dispatch. | [script/joanna_dispatch.yaml](script/joanna_dispatch.yaml), [script/notify_engine.yaml](script/notify_engine.yaml), [script/speech_engine.yaml](script/speech_engine.yaml) |
| Scenes | Seasonal and ambiance presets that the scripts and automations call into. | [scene/monthly_colors.yaml](scene/monthly_colors.yaml), [scene/living_room.yaml](scene/living_room.yaml) |
| Templates & Speech | Human-friendly voice briefings and templated responses. | [templates/speech/briefing.yaml](templates/speech/briefing.yaml) |
| Dashboards & Media | UI chrome, floorplans, sound bites, and automation assets. | [www/custom_ui/floorplan/images/branding/Bear-Stone-Docker-Diagram.jpg](www/custom_ui/floorplan/images/branding/Bear-Stone-Docker-Diagram.jpg), [media/](media) |

@ -49,10 +49,12 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
| [docker_infrastructure.yaml](docker_infrastructure.yaml) | Docker host patching telemetry + container/stack Repairs automation, 20-minute Joanna escalation for persistent container outages using stable configured monitor membership, and weekly scheduled prune actions across docker_10/14/17/69. | `sensor.docker_*_apt_status`, `binary_sensor.*_stack_status`, `sensor.docker_stacks_down_count`, `repairs.create`, `script.joanna_dispatch` |
| [github_watched_repo_scout.yaml](github_watched_repo_scout.yaml) | Nightly Joanna dispatch that reviews unread notifications from watched GitHub repos, recommends HA-config ideas, refreshes strong-candidate issues, and marks processed watched-repo notifications read. | `automation.github_watched_repo_scout_nightly`, `script.joanna_dispatch`, `script.send_to_logbook` |
| [proxmox.yaml](proxmox.yaml) | Proxmox runtime and disk pressure monitoring with Repairs for node degradations plus nightly Frigate reboot. | `binary_sensor.proxmox*_runtime_healthy`, `sensor.proxmox*_disk_used_percentage`, `repairs.create`, `button.qemu_docker2_101_reboot` |
| [synology_dsm.yaml](synology_dsm.yaml) | Synology DSM integration health normalization for Carlo-NAS01 and Carlo-NVR, with Repairs + Joanna dispatch on sustained integration, security, or storage problems. | `binary_sensor.carlo_*_synology_problem`, `sensor.carlo_*_synology_problem_summary`, `repairs.create`, `script.joanna_dispatch` |
| [infrastructure_observability.yaml](infrastructure_observability.yaml) | Normalized WAN/DNS/backup/domain/cert health + website uptime/latency SLO signals for Infrastructure dashboards. | `binary_sensor.infra_website_uptime_slo_breach`, `binary_sensor.infra_website_latency_degraded`, `binary_sensor.infra_*` |
| [onenote_indexer.yaml](onenote_indexer.yaml) | OneNote indexer health/status monitoring for Joanna, failure-repair automation, and a daily duplicate-delete maintenance request. | `sensor.onenote_indexer_last_job_status`, `binary_sensor.onenote_indexer_last_job_successful` |
| [mqtt_status.yaml](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` |
| [mariadb.yaml](mariadb.yaml) | MariaDB recorder health and capacity SQL sensors. | `sensor.mariadb_status`, `sensor.database_size` |
| [processmonitor.yaml](processmonitor.yaml) | Root filesystem disk-pressure monitoring with early Joanna review at 80% and Repairs + urgent dispatch at 90%. | `sensor.disk_use_percent`, `repairs.create`, `script.joanna_dispatch`, `tts.clear_cache` |
| [tugtainer_updates.yaml](tugtainer_updates.yaml) | Tugtainer container update notifications via webhook + persistent alerts, plus event-based Joanna dispatch when reports include `### Available:` (24h cooldown via `mode: single` + delay, no new helpers). | `persistent_notification.create`, `event: tugtainer_available_detected`, `script.joanna_dispatch`, `input_datetime.tugtainer_last_update` |
| [bearclaw.yaml](bearclaw.yaml) | Joanna/BearClaw bridge automations that forward Telegram commands to codex_appliance, include LLM-first routing context for freeform text, relay replies back, ingest `/api/bearclaw/status` telemetry, and expose dispatch plus QMD/memory-index sensors for Infrastructure dashboards. | `rest_command.bearclaw_*`, `sensor.bearclaw_status_telemetry`, `sensor.joanna_*`, `binary_sensor.joanna_*`, `automation.bearclaw_*`, `script.send_to_logbook` |
| [telegram_bot.yaml](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` |

@ -1,59 +1,129 @@
#-------------------------------------------
# @CCOSTAN
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# Process Monitor - Track HA-related services and processes.
#-------------------------------------------
######################################################################
## Process status sensors and notifications.
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Process Monitor - Disk pressure alerting + Joanna dispatch
# Tracks Home Assistant root filesystem usage from the System Monitor integration.
# -------------------------------------------------------------------
# - Blog: https://www.vcloudinfo.com/2026/04/joanna-agent-engineer-home-assistant-infrastructure-dispatch.html
# Notes: Uses `sensor.disk_use_percent` for the root (`/`) filesystem.
# Notes: 80% usage triggers cleanup-oriented notification + Joanna review.
# Notes: 90% usage opens a Repairs issue and dispatches Joanna for urgent triage.
######################################################################
# homeassistant:
# customize:
# sensor.process_mosquitto:
# friendly_name: 'Mosquitto'
#
# Uses SYSTEMMONITOR integration
#-------------------------------------------
#-------------------------------------------
##############################################################################
### Automations - Detect when things are not right. Like any Good Watchdog.
##############################################################################
automation:
- alias: "Self Heal Disk Use Alarm"
id: b16f2155-4688-4c0f-9cf8-b382e294a029
description: "Warn on elevated root disk usage and request Joanna review before it becomes critical."
mode: single
trigger:
- platform: numeric_state
entity_id: sensor.disk_use_percent
above: 80
variables:
mount_path: "/"
disk_use: "{{ states('sensor.disk_use_percent') | float(0) | round(1) }}"
trigger_context: "HA automation b16f2155-4688-4c0f-9cf8-b382e294a029 (Self Heal Disk Use Alarm)"
action:
- service: script.notify_engine
data:
value1: 'Hard Drive Monitor:'
value2: "Your harddrive is running out of Space! /dev/root:{{ states.sensor.disk_use_percent.state }}%!"
value3: 'Attempting to clean'
who: 'carlo'
value1: "Hard Drive Monitor:"
value2: "Your harddrive is running out of Space! {{ mount_path }}:{{ disk_use }}%!"
value3: "Attempting to clean"
who: "carlo"
- service: script.send_to_logbook
data:
topic: "SYSTEM"
message: "Disk usage exceeded 80% (/dev/root: {{ states.sensor.disk_use_percent.state }}%). Attempting to clean."
message: "Disk usage exceeded 80% ({{ mount_path }}: {{ disk_use }}%). Attempting to clean."
- service: tts.clear_cache
- condition: template
value_template: "{{ disk_use | float(0) < 90 }}"
- service: script.joanna_dispatch
data:
trigger_context: "{{ trigger_context }}"
source: "home_assistant_automation.self_heal_disk_use_alarm"
summary: "Home Assistant root disk usage exceeded 80%"
entity_ids:
- "sensor.disk_use_percent"
diagnostics: >-
mount_path={{ mount_path }},
disk_use={{ disk_use }},
threshold=80
request: >-
Review Home Assistant disk growth and recommend safe cleanup actions.
Check recorder/database size, logs, cache, backups, and temporary files.
Do not restart Home Assistant or remove data unless explicitly requested.
- alias: "Disk Use Alarm"
id: 1ce3cb43-0e27-4c53-acdd-d672396f3559
description: "Open a Repairs issue and dispatch Joanna when root disk usage becomes critical."
mode: single
trigger:
- platform: numeric_state
entity_id: sensor.disk_use_percent
above: 90
variables:
issue_id: "processmonitor_disk_use_critical"
mount_path: "/"
disk_use: "{{ states('sensor.disk_use_percent') | float(0) | round(1) }}"
trigger_context: "HA automation 1ce3cb43-0e27-4c53-acdd-d672396f3559 (Disk Use Alarm)"
action:
- service: script.notify_engine
data:
value1: 'Hard Drive Monitor:'
value2: "Your harddrive is running out of Space! /dev/root:{{ states.sensor.disk_use_percent.state }}%!"
who: 'carlo'
value1: "Hard Drive Monitor:"
value2: "Your harddrive is running out of Space! {{ mount_path }}:{{ disk_use }}%!"
who: "carlo"
- service: script.send_to_logbook
data:
topic: "SYSTEM"
message: >-
Disk usage exceeded 90% ({{ mount_path }}: {{ disk_use }}%).
Repair {{ issue_id }} opened and Joanna investigation requested.
- service: repairs.create
data:
issue_id: "{{ issue_id }}"
title: "Home Assistant disk usage critical"
severity: "error"
persistent: true
description: >-
Home Assistant detected critical disk pressure on {{ mount_path }}.
disk_use: {{ disk_use }}%
entity_id: sensor.disk_use_percent
- service: script.joanna_dispatch
data:
trigger_context: "{{ trigger_context }}"
source: "home_assistant_automation.disk_use_alarm"
summary: "Home Assistant root disk usage exceeded 90%"
entity_ids:
- "sensor.disk_use_percent"
diagnostics: >-
issue_id={{ issue_id }},
mount_path={{ mount_path }},
disk_use={{ disk_use }},
threshold=90
request: >-
Investigate critical Home Assistant disk usage and recommend or perform safe remediation if available.
Check recorder/database size, logs, cache, backups, and temporary files first.
Do not restart Home Assistant or prune/delete data unless explicitly requested.
- alias: "Disk Use Alarm Recovery"
id: processmonitor_disk_use_alarm_recovery
description: "Clear the disk pressure repair once root filesystem usage has recovered."
mode: single
trigger:
- platform: numeric_state
entity_id: sensor.disk_use_percent
below: 85
for:
minutes: 10
action:
- service: repairs.remove
continue_on_error: true
data:
issue_id: "processmonitor_disk_use_critical"
- service: script.send_to_logbook
data:
topic: "SYSTEM"
message: "Disk usage exceeded 90% (/dev/root: {{ states.sensor.disk_use_percent.state }}%)."
message: "Disk usage recovered below 85% on /. Repair processmonitor_disk_use_critical cleared."

@ -0,0 +1,486 @@
######################################################################
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Synology DSM Monitoring - NAS health normalization + Joanna dispatch
# Reviews Synology DSM integration health and escalates sustained NAS problems.
# -------------------------------------------------------------------
# Notes: Uses native `synology_dsm` entities for Carlo-NAS01 and Carlo-NVR.
# Notes: Joanna dispatches are reserved for integration/security/storage problems, not routine reboot/shutdown controls.
# Notes: DSM update availability stays diagnostic context only; it does not trigger remediation by itself.
######################################################################
template:
- binary_sensor:
- name: "Carlo-NAS01 Synology Integration Problem"
unique_id: carlo_nas01_synology_integration_problem
device_class: problem
state: >-
{% set ids = [
'binary_sensor.carlo_nas01_security_status',
'sensor.carlo_nas01_volume_1_status',
'sensor.carlo_nas01_volume_1_volume_used',
'sensor.carlo_nas01_drive_1_status',
'sensor.carlo_nas01_drive_2_status',
'sensor.carlo_nas01_drive_3_status',
'binary_sensor.carlo_nas01_drive_1_below_min_remaining_life',
'binary_sensor.carlo_nas01_drive_2_below_min_remaining_life',
'binary_sensor.carlo_nas01_drive_3_below_min_remaining_life',
'binary_sensor.carlo_nas01_drive_1_exceeded_max_bad_sectors',
'binary_sensor.carlo_nas01_drive_2_exceeded_max_bad_sectors',
'binary_sensor.carlo_nas01_drive_3_exceeded_max_bad_sectors',
'update.carlo_nas01_dsm_update'
] %}
{% set ns = namespace(problem=false) %}
{% for id in ids %}
{% if states(id) in ['unknown', 'unavailable'] %}
{% set ns.problem = true %}
{% endif %}
{% endfor %}
{{ ns.problem }}
- name: "Carlo-NAS01 Synology Security Problem"
unique_id: carlo_nas01_synology_security_problem
device_class: problem
state: >-
{{ is_state('binary_sensor.carlo_nas01_security_status', 'on') }}
- name: "Carlo-NAS01 Synology Storage Problem"
unique_id: carlo_nas01_synology_storage_problem
device_class: problem
state: >-
{% set volume_status = states('sensor.carlo_nas01_volume_1_status') | lower %}
{% set volume_used_raw = states('sensor.carlo_nas01_volume_1_volume_used') %}
{% set volume_used = volume_used_raw | float(0) if volume_used_raw not in ['unknown', 'unavailable', 'none', ''] else 0 %}
{{ volume_status not in ['normal', 'unknown', 'unavailable', 'none', ''] or
volume_used >= 85 or
states('sensor.carlo_nas01_drive_1_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nas01_drive_2_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nas01_drive_3_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
is_state('binary_sensor.carlo_nas01_drive_1_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_2_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_3_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_1_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nas01_drive_2_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nas01_drive_3_exceeded_max_bad_sectors', 'on') }}
- name: "Carlo-NAS01 Synology Problem"
unique_id: carlo_nas01_synology_problem
device_class: problem
state: >-
{{ is_state('binary_sensor.carlo_nas01_synology_integration_problem', 'on') or
is_state('binary_sensor.carlo_nas01_synology_security_problem', 'on') or
is_state('binary_sensor.carlo_nas01_synology_storage_problem', 'on') }}
- name: "Carlo-NVR Synology Integration Problem"
unique_id: carlo_nvr_synology_integration_problem
device_class: problem
state: >-
{% set ids = [
'binary_sensor.carlo_nvr_security_status',
'sensor.carlo_nvr_volume_1_status',
'sensor.carlo_nvr_volume_1_volume_used',
'sensor.carlo_nvr_drive_1_status',
'sensor.carlo_nvr_drive_2_status',
'binary_sensor.carlo_nvr_drive_1_below_min_remaining_life',
'binary_sensor.carlo_nvr_drive_2_below_min_remaining_life',
'binary_sensor.carlo_nvr_drive_1_exceeded_max_bad_sectors',
'binary_sensor.carlo_nvr_drive_2_exceeded_max_bad_sectors',
'update.carlo_nvr_dsm_update'
] %}
{% set ns = namespace(problem=false) %}
{% for id in ids %}
{% if states(id) in ['unknown', 'unavailable'] %}
{% set ns.problem = true %}
{% endif %}
{% endfor %}
{{ ns.problem }}
- name: "Carlo-NVR Synology Security Problem"
unique_id: carlo_nvr_synology_security_problem
device_class: problem
state: >-
{{ is_state('binary_sensor.carlo_nvr_security_status', 'on') }}
- name: "Carlo-NVR Synology Storage Problem"
unique_id: carlo_nvr_synology_storage_problem
device_class: problem
state: >-
{% set volume_status = states('sensor.carlo_nvr_volume_1_status') | lower %}
{% set volume_used_raw = states('sensor.carlo_nvr_volume_1_volume_used') %}
{% set volume_used = volume_used_raw | float(0) if volume_used_raw not in ['unknown', 'unavailable', 'none', ''] else 0 %}
{{ volume_status not in ['normal', 'unknown', 'unavailable', 'none', ''] or
volume_used >= 85 or
states('sensor.carlo_nvr_drive_1_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nvr_drive_2_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
is_state('binary_sensor.carlo_nvr_drive_1_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nvr_drive_2_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nvr_drive_1_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nvr_drive_2_exceeded_max_bad_sectors', 'on') }}
- name: "Carlo-NVR Synology Problem"
unique_id: carlo_nvr_synology_problem
device_class: problem
state: >-
{{ is_state('binary_sensor.carlo_nvr_synology_integration_problem', 'on') or
is_state('binary_sensor.carlo_nvr_synology_security_problem', 'on') or
is_state('binary_sensor.carlo_nvr_synology_storage_problem', 'on') }}
- sensor:
- name: "Carlo-NAS01 Synology Problem Severity"
unique_id: carlo_nas01_synology_problem_severity
state: >-
{% set volume_status = states('sensor.carlo_nas01_volume_1_status') | lower %}
{% set volume_used_raw = states('sensor.carlo_nas01_volume_1_volume_used') %}
{% set volume_used = volume_used_raw | float(0) if volume_used_raw not in ['unknown', 'unavailable', 'none', ''] else 0 %}
{% set hard_storage = volume_status not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nas01_drive_1_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nas01_drive_2_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nas01_drive_3_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
is_state('binary_sensor.carlo_nas01_drive_1_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_2_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_3_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nas01_drive_1_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nas01_drive_2_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nas01_drive_3_exceeded_max_bad_sectors', 'on') or
volume_used >= 92 %}
{% if is_state('binary_sensor.carlo_nas01_synology_integration_problem', 'on') or
is_state('binary_sensor.carlo_nas01_synology_security_problem', 'on') or
hard_storage %}
error
{% elif is_state('binary_sensor.carlo_nas01_synology_storage_problem', 'on') %}
warning
{% else %}
none
{% endif %}
- name: "Carlo-NAS01 Synology Problem Summary"
unique_id: carlo_nas01_synology_problem_summary
state: >-
{% set ns = namespace(items=[]) %}
{% if states('binary_sensor.carlo_nas01_security_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['security sensor unavailable'] %}
{% elif is_state('binary_sensor.carlo_nas01_security_status', 'on') %}
{% set ns.items = ns.items + ['security advisor reported an unsafe state'] %}
{% endif %}
{% set volume_status = states('sensor.carlo_nas01_volume_1_status') | lower %}
{% if volume_status in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['volume status unavailable'] %}
{% elif volume_status not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['volume status=' ~ volume_status] %}
{% endif %}
{% set volume_used = states('sensor.carlo_nas01_volume_1_volume_used') %}
{% if volume_used in ['unknown', 'unavailable', 'none', ''] %}
{% set ns.items = ns.items + ['volume used unavailable'] %}
{% elif volume_used | float(0) >= 85 %}
{% set ns.items = ns.items + ['volume used=' ~ (volume_used | float(0) | round(1)) ~ '%'] %}
{% endif %}
{% if states('sensor.carlo_nas01_drive_1_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['drive 1 status unavailable'] %}
{% elif states('sensor.carlo_nas01_drive_1_status') | lower not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['drive 1 status=' ~ (states('sensor.carlo_nas01_drive_1_status') | lower)] %}
{% endif %}
{% if states('sensor.carlo_nas01_drive_2_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['drive 2 status unavailable'] %}
{% elif states('sensor.carlo_nas01_drive_2_status') | lower not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['drive 2 status=' ~ (states('sensor.carlo_nas01_drive_2_status') | lower)] %}
{% endif %}
{% if states('sensor.carlo_nas01_drive_3_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['drive 3 status unavailable'] %}
{% elif states('sensor.carlo_nas01_drive_3_status') | lower not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['drive 3 status=' ~ (states('sensor.carlo_nas01_drive_3_status') | lower)] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_1_below_min_remaining_life', 'on') %}
{% set ns.items = ns.items + ['drive 1 below remaining life threshold'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_2_below_min_remaining_life', 'on') %}
{% set ns.items = ns.items + ['drive 2 below remaining life threshold'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_3_below_min_remaining_life', 'on') %}
{% set ns.items = ns.items + ['drive 3 below remaining life threshold'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_1_exceeded_max_bad_sectors', 'on') %}
{% set ns.items = ns.items + ['drive 1 exceeded max bad sectors'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_2_exceeded_max_bad_sectors', 'on') %}
{% set ns.items = ns.items + ['drive 2 exceeded max bad sectors'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nas01_drive_3_exceeded_max_bad_sectors', 'on') %}
{% set ns.items = ns.items + ['drive 3 exceeded max bad sectors'] %}
{% endif %}
{{ ns.items | join('; ') if ns.items else 'ok' }}
- name: "Carlo-NVR Synology Problem Severity"
unique_id: carlo_nvr_synology_problem_severity
state: >-
{% set volume_status = states('sensor.carlo_nvr_volume_1_status') | lower %}
{% set volume_used_raw = states('sensor.carlo_nvr_volume_1_volume_used') %}
{% set volume_used = volume_used_raw | float(0) if volume_used_raw not in ['unknown', 'unavailable', 'none', ''] else 0 %}
{% set hard_storage = volume_status not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nvr_drive_1_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
states('sensor.carlo_nvr_drive_2_status') | lower not in ['normal', 'unknown', 'unavailable', 'none', ''] or
is_state('binary_sensor.carlo_nvr_drive_1_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nvr_drive_2_below_min_remaining_life', 'on') or
is_state('binary_sensor.carlo_nvr_drive_1_exceeded_max_bad_sectors', 'on') or
is_state('binary_sensor.carlo_nvr_drive_2_exceeded_max_bad_sectors', 'on') or
volume_used >= 92 %}
{% if is_state('binary_sensor.carlo_nvr_synology_integration_problem', 'on') or
is_state('binary_sensor.carlo_nvr_synology_security_problem', 'on') or
hard_storage %}
error
{% elif is_state('binary_sensor.carlo_nvr_synology_storage_problem', 'on') %}
warning
{% else %}
none
{% endif %}
- name: "Carlo-NVR Synology Problem Summary"
unique_id: carlo_nvr_synology_problem_summary
state: >-
{% set ns = namespace(items=[]) %}
{% if states('binary_sensor.carlo_nvr_security_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['security sensor unavailable'] %}
{% elif is_state('binary_sensor.carlo_nvr_security_status', 'on') %}
{% set ns.items = ns.items + ['security advisor reported an unsafe state'] %}
{% endif %}
{% set volume_status = states('sensor.carlo_nvr_volume_1_status') | lower %}
{% if volume_status in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['volume status unavailable'] %}
{% elif volume_status not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['volume status=' ~ volume_status] %}
{% endif %}
{% set volume_used = states('sensor.carlo_nvr_volume_1_volume_used') %}
{% if volume_used in ['unknown', 'unavailable', 'none', ''] %}
{% set ns.items = ns.items + ['volume used unavailable'] %}
{% elif volume_used | float(0) >= 85 %}
{% set ns.items = ns.items + ['volume used=' ~ (volume_used | float(0) | round(1)) ~ '%'] %}
{% endif %}
{% if states('sensor.carlo_nvr_drive_1_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['drive 1 status unavailable'] %}
{% elif states('sensor.carlo_nvr_drive_1_status') | lower not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['drive 1 status=' ~ (states('sensor.carlo_nvr_drive_1_status') | lower)] %}
{% endif %}
{% if states('sensor.carlo_nvr_drive_2_status') in ['unknown', 'unavailable'] %}
{% set ns.items = ns.items + ['drive 2 status unavailable'] %}
{% elif states('sensor.carlo_nvr_drive_2_status') | lower not in ['normal', 'none', ''] %}
{% set ns.items = ns.items + ['drive 2 status=' ~ (states('sensor.carlo_nvr_drive_2_status') | lower)] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nvr_drive_1_below_min_remaining_life', 'on') %}
{% set ns.items = ns.items + ['drive 1 below remaining life threshold'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nvr_drive_2_below_min_remaining_life', 'on') %}
{% set ns.items = ns.items + ['drive 2 below remaining life threshold'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nvr_drive_1_exceeded_max_bad_sectors', 'on') %}
{% set ns.items = ns.items + ['drive 1 exceeded max bad sectors'] %}
{% endif %}
{% if is_state('binary_sensor.carlo_nvr_drive_2_exceeded_max_bad_sectors', 'on') %}
{% set ns.items = ns.items + ['drive 2 exceeded max bad sectors'] %}
{% endif %}
{{ ns.items | join('; ') if ns.items else 'ok' }}
automation:
- id: synology_dsm_open_repair_and_dispatch
alias: "Synology DSM - Open Repair And Dispatch"
description: "Open a Repairs issue and dispatch Joanna when a Synology problem stays active."
mode: queued
trigger:
- platform: state
entity_id:
- binary_sensor.carlo_nas01_synology_problem
- binary_sensor.carlo_nvr_synology_problem
to: "on"
for: "00:10:00"
variables:
host_name: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
Carlo-NAS01
{% else %}
Carlo-NVR
{% endif %}
issue_id: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
synology_carlo_nas01_problem
{% else %}
synology_carlo_nvr_problem
{% endif %}
source: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
home_assistant_automation.synology_dsm_open_repair_and_dispatch.carlo_nas01
{% else %}
home_assistant_automation.synology_dsm_open_repair_and_dispatch.carlo_nvr
{% endif %}
ssh_alias: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
nas11
{% else %}
nas12
{% endif %}
dsm_url: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
http://192.168.10.11:5000
{% else %}
https://192.168.10.12:5001
{% endif %}
severity_sensor: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
sensor.carlo_nas01_synology_problem_severity
{% else %}
sensor.carlo_nvr_synology_problem_severity
{% endif %}
summary_sensor: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
sensor.carlo_nas01_synology_problem_summary
{% else %}
sensor.carlo_nvr_synology_problem_summary
{% endif %}
security_entity: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
binary_sensor.carlo_nas01_security_status
{% else %}
binary_sensor.carlo_nvr_security_status
{% endif %}
volume_status_entity: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
sensor.carlo_nas01_volume_1_status
{% else %}
sensor.carlo_nvr_volume_1_status
{% endif %}
volume_used_entity: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
sensor.carlo_nas01_volume_1_volume_used
{% else %}
sensor.carlo_nvr_volume_1_volume_used
{% endif %}
update_entity: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
update.carlo_nas01_dsm_update
{% else %}
update.carlo_nvr_dsm_update
{% endif %}
entity_ids: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
binary_sensor.carlo_nas01_synology_problem,
binary_sensor.carlo_nas01_synology_integration_problem,
binary_sensor.carlo_nas01_synology_security_problem,
binary_sensor.carlo_nas01_synology_storage_problem,
sensor.carlo_nas01_synology_problem_severity,
sensor.carlo_nas01_synology_problem_summary,
binary_sensor.carlo_nas01_security_status,
sensor.carlo_nas01_volume_1_status,
sensor.carlo_nas01_volume_1_volume_used,
sensor.carlo_nas01_drive_1_status,
sensor.carlo_nas01_drive_2_status,
sensor.carlo_nas01_drive_3_status,
binary_sensor.carlo_nas01_drive_1_below_min_remaining_life,
binary_sensor.carlo_nas01_drive_2_below_min_remaining_life,
binary_sensor.carlo_nas01_drive_3_below_min_remaining_life,
binary_sensor.carlo_nas01_drive_1_exceeded_max_bad_sectors,
binary_sensor.carlo_nas01_drive_2_exceeded_max_bad_sectors,
binary_sensor.carlo_nas01_drive_3_exceeded_max_bad_sectors,
update.carlo_nas01_dsm_update
{% else %}
binary_sensor.carlo_nvr_synology_problem,
binary_sensor.carlo_nvr_synology_integration_problem,
binary_sensor.carlo_nvr_synology_security_problem,
binary_sensor.carlo_nvr_synology_storage_problem,
sensor.carlo_nvr_synology_problem_severity,
sensor.carlo_nvr_synology_problem_summary,
binary_sensor.carlo_nvr_security_status,
sensor.carlo_nvr_volume_1_status,
sensor.carlo_nvr_volume_1_volume_used,
sensor.carlo_nvr_drive_1_status,
sensor.carlo_nvr_drive_2_status,
binary_sensor.carlo_nvr_drive_1_below_min_remaining_life,
binary_sensor.carlo_nvr_drive_2_below_min_remaining_life,
binary_sensor.carlo_nvr_drive_1_exceeded_max_bad_sectors,
binary_sensor.carlo_nvr_drive_2_exceeded_max_bad_sectors,
update.carlo_nvr_dsm_update
{% endif %}
problem_severity: "{{ states(severity_sensor) }}"
problem_summary: "{{ states(summary_sensor) }}"
security_state: "{{ states(security_entity) }}"
volume_status: "{{ states(volume_status_entity) }}"
volume_used: "{{ states(volume_used_entity) }}"
dsm_update_state: "{{ states(update_entity) }}"
trigger_context: "HA automation synology_dsm_open_repair_and_dispatch (Synology DSM - Open Repair And Dispatch)"
action:
- service: repairs.create
data:
issue_id: "{{ issue_id }}"
title: "{{ host_name }} Synology health issue"
severity: "{{ 'error' if problem_severity == 'error' else 'warning' }}"
persistent: true
description: >-
Home Assistant detected a sustained Synology DSM issue for {{ host_name }}.
summary: {{ problem_summary }}
security_state: {{ security_state }}
volume_status: {{ volume_status }}
volume_used: {{ volume_used }}
dsm_update: {{ dsm_update_state }}
ssh_alias: {{ ssh_alias }}
dsm_url: {{ dsm_url }}
- service: script.send_to_logbook
data:
topic: "SYNOLOGY"
message: >-
{{ host_name }} reported a Synology DSM problem for 10 minutes.
Repair {{ issue_id }} opened and Joanna investigation requested.
Summary: {{ problem_summary }}.
- service: script.joanna_dispatch
data:
trigger_context: "{{ trigger_context }}"
source: "{{ source }}"
summary: "{{ host_name }} Synology DSM problem detected"
entity_ids: "{{ entity_ids }}"
diagnostics: >-
issue_id={{ issue_id }},
severity={{ problem_severity }},
problem_sensor={{ trigger.entity_id }},
problem_summary={{ problem_summary }},
security_state={{ security_state }},
volume_status={{ volume_status }},
volume_used={{ volume_used }},
dsm_update={{ dsm_update_state }},
ssh_alias={{ ssh_alias }},
dsm_url={{ dsm_url }}
request: >-
Investigate {{ host_name }} using the Home Assistant Synology DSM entities first, then DSM or SSH if needed.
Review security status, drive health, volume health, and integration availability.
Do not reboot or shut down the NAS unless explicitly requested.
- id: synology_dsm_clear_repair_on_recovery
alias: "Synology DSM - Clear Repair On Recovery"
description: "Clear the Synology Repairs issue once the monitored problem has recovered."
mode: queued
trigger:
- platform: state
entity_id:
- binary_sensor.carlo_nas01_synology_problem
- binary_sensor.carlo_nvr_synology_problem
to: "off"
for: "00:05:00"
variables:
host_name: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
Carlo-NAS01
{% else %}
Carlo-NVR
{% endif %}
issue_id: >-
{% if trigger.entity_id == 'binary_sensor.carlo_nas01_synology_problem' %}
synology_carlo_nas01_problem
{% else %}
synology_carlo_nvr_problem
{% endif %}
action:
- service: repairs.remove
continue_on_error: true
data:
issue_id: "{{ issue_id }}"
- service: script.send_to_logbook
data:
topic: "SYNOLOGY"
message: "{{ host_name }} Synology DSM health recovered. Repair {{ issue_id }} cleared."

@ -16,7 +16,7 @@
</div>
Reusable scripts that other automations call for notifications, lighting, and safety responses. Pass variables in; let the script do the heavy lifting.
Reusable scripts that other automations call for notifications, lighting, safety responses, and Joanna/BearClaw AGENT engineer handoffs. Pass variables in; let the script do the heavy lifting.
### Quick navigation
- You are here: `config/script/` (scripts library)
@ -30,30 +30,43 @@ Reusable scripts that other automations call for notifications, lighting, and sa
| [notify_engine.yaml](notify_engine.yaml) | Single entrypoint for rich push notifications. |
| [notify_live_activity.yaml](notify_live_activity.yaml) | Shared helper for tagged live activity/live update pushes and clear commands. |
| [send_to_logbook.yaml](send_to_logbook.yaml) | Generic `logbook.log` helper for Activity feed entries (Issue #1550). |
| [joanna_dispatch.yaml](joanna_dispatch.yaml) | Shared BearClaw/Joanna dispatch schema for automation remediation requests. |
| [joanna_dispatch.yaml](joanna_dispatch.yaml) | Shared AGENT engineer dispatch contract that routes HA-detected issues into Joanna/BearClaw remediation. |
| [speech_engine.yaml](speech_engine.yaml) | TTS/announcement orchestration with templated speech. |
| [monthly_color_scene.yaml](monthly_color_scene.yaml) | Seasonal lighting scenes used across automations. |
| [interior_off.yaml](interior_off.yaml) | One-call <EFBFBD>all interior lights off<66> helper. |
| [interior_off.yaml](interior_off.yaml) | One-call "all interior lights off" helper. |
### Joanna + BearClaw automated resolution flow
`script.joanna_dispatch` is the shared handoff contract from Home Assistant automations to BearClaw/Joanna.
### Joanna + BearClaw AGENT engineer handoff
`script.joanna_dispatch` is the shared handoff contract from Home Assistant automations into Joanna/BearClaw when Home Assistant detects something worth investigating or fixing.
Why we use it:
- Keeps one message schema for remediation context (`trigger_context`, `source`, `summary`, `entity_ids`, `diagnostics`, `request`).
- Avoids repeating direct `rest_command.bearclaw_command` payload formatting in multiple packages.
- Lets Home Assistant stay focused on detection, timing, and routing while Joanna acts as the AGENT engineer for infrastructure triage and recommended remediation.
- Makes resolution-trigger automations easier to review, update, and audit.
What the helper normalizes before the BearClaw intake call:
- `trigger_context`, `source`, and `summary` so every dispatch has traceable origin details.
- `entity_ids` from either a YAML list or a comma-delimited string.
- `diagnostics` from either free text or structured mappings/sequences.
- `request` guardrails so Joanna defaults to investigation/recommendation, not blind resets or power-cycles.
Current automations that kick off automated resolutions (via `script.joanna_dispatch`):
| Automation ID | Alias | File |
| --- | --- | --- |
| `github_watched_repo_scout_nightly` | GitHub Watched Repo Scout - Nightly Joanna Review | [../packages/github_watched_repo_scout.yaml](../packages/github_watched_repo_scout.yaml) |
| `mqtt_open_repair_on_failure` | MQTT - Open Repair On Failure | [../packages/mqtt_status.yaml](../packages/mqtt_status.yaml) |
| `onenote_indexer_daily_delete_maintenance` | OneNote Indexer - Daily Delete Maintenance Request | [../packages/onenote_indexer.yaml](../packages/onenote_indexer.yaml) |
| `onenote_indexer_failure_open_repair` | OneNote Indexer - Open Repair On Failure | [../packages/onenote_indexer.yaml](../packages/onenote_indexer.yaml) |
| `infra_backup_nightly_verification` | Infrastructure - Backup Nightly Verification | [../packages/infrastructure_observability.yaml](../packages/infrastructure_observability.yaml) |
| `docker_state_sync_repairs_dynamic` | Docker State Sync - Repairs (Dynamic) | [../packages/docker_infrastructure.yaml](../packages/docker_infrastructure.yaml) |
| `docker_group_reconcile_weekly_joanna_review` | Docker Group Reconcile - Weekly Joanna Review | [../packages/docker_infrastructure.yaml](../packages/docker_infrastructure.yaml) |
| `unifi_ap_no_clients_repair_combined` | Unifi AP Create Repair Issue after 5m of 0 Clients | [../packages/wireless.yaml](../packages/wireless.yaml) |
| `synology_dsm_open_repair_and_dispatch` | Synology DSM - Open Repair And Dispatch | [../packages/synology_dsm.yaml](../packages/synology_dsm.yaml) |
| `b16f2155-4688-4c0f-9cf8-b382e294a029` | Self Heal Disk Use Alarm | [../packages/processmonitor.yaml](../packages/processmonitor.yaml) |
| `1ce3cb43-0e27-4c53-acdd-d672396f3559` | Disk Use Alarm | [../packages/processmonitor.yaml](../packages/processmonitor.yaml) |
### Tips
- Keep scripts generic<69>route data via `data:`/`variables:` and reuse everywhere.
- Keep scripts generic, route data via `data:`/`variables:`, and reuse everywhere.
- If you copy a script, rename any `alias` and `id` fields to avoid duplicates.
**All of my configuration files are tested against the most stable version of home-assistant.**

Loading…
Cancel
Save

Powered by TurnKey Linux.