Enhance automation configurations and documentation

- Updated dark_rainy_day.yaml to improve weather-related light automation with a more robust trigger mapping.
- Refined docker_infrastructure.yaml to include a 20-minute escalation for persistent container outages and updated related documentation.
- Revised onenote_indexer.yaml to clarify daily maintenance request details and improve communication style.
- Enhanced space.yaml to track SpaceX launches with improved trigger logic and messaging.
- Updated README.md and script documentation to reflect new features and automation enhancements.
feature/powerwall-live-activity-1598
Carlo Costanzo 1 week ago
parent 4366820d02
commit 314d5d47aa

@ -1,5 +1,13 @@
######################################################################
## Dark House Little extra light - DARK and Cloudy or just rainy.
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Dark House Little Extra Light - Daytime rain/lightning lighting helper
# Turns on living-room lights and announces weather-related reasons.
# -------------------------------------------------------------------
# Notes:
# Avoid trigger entity string-splitting; map trigger IDs safely.
######################################################################
- alias: 'Dark House Little extra light'
@ -60,7 +68,16 @@
- service: script.speech_engine
data:
value1: "Because of the {{trigger.entity_id.split('_')[2]|replace('precip','rain')|replace('counter','lightning')|replace('carlo','rain') }} {{trigger.entity_id.split('_')[3]|replace('intensity',' ')| replace('carlo','and clouds')}} outside. I will turn on some extra lights in the living room."
value1: >-
{% set reason_map = {
'sensor.pirateweather_precip': 'rain',
'sensor.pirateweather_precip_intensity': 'heavy rain',
'sensor.blitzortung_lightning_counter': 'lightning',
'group.family': 'rain and clouds',
'binary_sensor.sleepnumber_carlo_carlo_is_in_bed': 'rain and clouds'
} %}
{% set reason = reason_map.get(trigger.entity_id | default(''), 'weather changes') %}
Because of the {{ reason }} outside. I will turn on some extra lights in the living room.
call_outside_weather: 1
call_window_check: 1
call_garage_check: 1

@ -45,7 +45,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this
| [lightning.yaml](lightning.yaml) | Blitzortung lightning counter monitoring with snoozeable push actions. | `sensor.blitzortung_lightning_counter`, `input_boolean.snooze_lightning`, notify engine actions |
| [logbook_activity_feed.yaml](logbook_activity_feed.yaml) | Dummy `sensor.activity_feed` + helper to write clean Activity entries (Issue #1550). | `sensor.activity_feed`, `script.send_to_logbook` |
| [mariadb_monitoring.yaml](mariadb_monitoring.yaml) | MariaDB health sensors and Lovelace dashboard snippet for recorder stats. | `sensor.mariadb_status`, `sensor.database_size` |
| [docker_infrastructure.yaml](docker_infrastructure.yaml) | Docker host patching telemetry + container/stack Repairs automation 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` |
| [docker_infrastructure.yaml](docker_infrastructure.yaml) | Docker host patching telemetry + container/stack Repairs automation, 20-minute Joanna escalation for persistent container outages, 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` |
| [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` |
| [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` |

@ -11,7 +11,7 @@
# Notes: Reboots are handled directly on each host by apt_weekly.sh.
# Notes: Reboot staggering: docker_14 first, docker_69 second, docker_10 third.
# Notes: Container monitoring is dynamic with binary_sensor status preferred over switch state.
# Notes: Includes Portainer stack status repairs and scheduled image prune.
# Notes: Includes Portainer stack status repairs, 20-minute Joanna dispatch for persistent container outages, and scheduled image prune.
######################################################################
input_datetime:
@ -257,7 +257,7 @@ template:
icon: mdi:docker
state: >-
{% set ns = namespace(keys=[], down=[]) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set telemetry_degraded = is_state('binary_sensor.docker_container_telemetry_degraded', 'on') %}
{% for switch_entity in monitored %}
{% set key = switch_entity | replace('switch.', '') | regex_replace('_container(?:_2)?$', '') %}
@ -297,19 +297,58 @@ template:
{% set ns.down = ns.down + [key] %}
{% endif %}
{% endfor %}
{{ ns.down | sort | join(', ') if (ns.down | count > 0) else 'none' }}
{{ 'ok' if (ns.down | count == 0) else 'down' }}
attributes:
down_containers: >-
{% set ns = namespace(keys=[], down=[]) %}
{% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list %}
{% set telemetry_degraded = is_state('binary_sensor.docker_container_telemetry_degraded', 'on') %}
{% for switch_entity in monitored %}
{% set key = switch_entity | replace('switch.', '') | regex_replace('_container(?:_2)?$', '') %}
{% if key not in ns.keys %}
{% set ns.keys = ns.keys + [key] %}
{% endif %}
{% endfor %}
{% for key in ns.keys | sort %}
{% set status_entity = 'binary_sensor.' ~ key ~ '_status' %}
{% set status_entity_alt = status_entity ~ '_2' %}
{% set state_entity = 'sensor.' ~ key ~ '_state' %}
{% set state_entity_alt = state_entity ~ '_2' %}
{% set switch_entity = 'switch.' ~ key ~ '_container' %}
{% set switch_entity_alt = switch_entity ~ '_2' %}
{% set resolver = namespace(state='unknown', chosen=false) %}
{% for candidate in [status_entity, status_entity_alt, state_entity, state_entity_alt, switch_entity, switch_entity_alt] %}
{% if not resolver.chosen and expand(candidate) | count > 0 %}
{% set candidate_state = states(candidate) | lower %}
{% if candidate_state not in ['unknown', 'unavailable', ''] %}
{% set resolver.state = candidate_state %}
{% set resolver.chosen = true %}
{% endif %}
{% endif %}
{% endfor %}
{% if not resolver.chosen %}
{% for candidate in [status_entity, status_entity_alt, state_entity, state_entity_alt, switch_entity, switch_entity_alt] %}
{% if not resolver.chosen and expand(candidate) | count > 0 %}
{% set resolver.state = states(candidate) | lower %}
{% set resolver.chosen = true %}
{% endif %}
{% endfor %}
{% endif %}
{% set effective_state = resolver.state %}
{% if effective_state in ['off', 'stopped'] %}
{% set ns.down = ns.down + [key] %}
{% elif not telemetry_degraded and effective_state in ['unknown', 'unavailable'] %}
{% set ns.down = ns.down + [key] %}
{% endif %}
{% endfor %}
{{ ns.down | sort }}
- name: "Docker Containers Down Count"
unique_id: docker_containers_down_count
icon: mdi:counter
state: >-
{% set down_list = states('sensor.docker_containers_down_list') %}
{% set normalized = down_list | lower %}
{% if normalized in ['unknown', 'unavailable', 'none', ''] %}
0
{% else %}
{{ down_list.split(',') | map('trim') | reject('equalto', '') | list | count }}
{% endif %}
{% set down_items = state_attr('sensor.docker_containers_down_list', 'down_containers') | default([], true) | list %}
{{ down_items | count }}
- name: "Docker Stacks Down List"
unique_id: docker_stacks_down_list
@ -475,6 +514,60 @@ script:
data:
topic: "DOCKER"
message: "{{ container_name }} is {{ effective_state }} for over 5 minutes."
- delay:
minutes: 15
- variables:
persistent_effective_state: >-
{% set candidates = [status_entity, status_entity_alt, state_entity, state_entity_alt, switch_entity, switch_entity_alt] %}
{% set resolver = namespace(state='unknown', chosen=false) %}
{% for candidate in candidates %}
{% if not resolver.chosen and expand(candidate) | count > 0 %}
{% set candidate_state = states(candidate) | lower %}
{% if candidate_state not in ['unknown', 'unavailable', ''] %}
{% set resolver.state = candidate_state %}
{% set resolver.chosen = true %}
{% endif %}
{% endif %}
{% endfor %}
{% if not resolver.chosen %}
{% for candidate in candidates %}
{% if not resolver.chosen and expand(candidate) | count > 0 %}
{% set resolver.state = states(candidate) | lower %}
{% set resolver.chosen = true %}
{% endif %}
{% endfor %}
{% endif %}
{{ resolver.state }}
container_name: "{{ state_attr(effective_entity, 'friendly_name') | default(container_key, true) }}"
- condition: template
value_template: >-
{{ persistent_effective_state in down_states and
not (is_state('binary_sensor.docker_container_telemetry_degraded', 'on') and
persistent_effective_state in ['unknown', 'unavailable']) }}
- condition: state
entity_id: binary_sensor.docker_container_alerts_snoozed
state: "off"
- service: script.joanna_dispatch
data:
trigger_context: >-
HA automation docker_state_sync_repairs_dynamic
(Docker State Sync - Repairs (Dynamic))
source: "home_assistant_automation.docker_state_sync_repairs_dynamic"
summary: "{{ container_name }} container has remained {{ persistent_effective_state }} for 20 minutes"
entity_ids:
- "{{ effective_entity }}"
- "{{ switch_entity }}"
diagnostics: >-
issue_id={{ issue_id }},
spook_issue_id={{ spook_issue_id }},
container_key={{ container_key }},
effective_entity={{ effective_entity }},
switch_entity={{ switch_entity }},
effective_state_initial={{ effective_state }},
effective_state_20m={{ persistent_effective_state }}
request: >-
Troubleshoot and resolve the persistent Docker container outage if possible.
Use Duplicati and the related host/container telemetry to verify recovery.
- conditions: "{{ op == 'clear' }}"
sequence:
- variables:
@ -695,37 +788,54 @@ automation:
- alias: "Docker State Sync - Repairs (Dynamic)"
id: docker_state_sync_repairs_dynamic
description: "Detect Docker container/stack state transitions and delegate Repairs sync."
mode: parallel
mode: queued
max: 50
max_exceeded: silent
trigger:
- platform: event
event_type: state_changed
variables:
entity_id: "{{ trigger.event.data.entity_id | default('') }}"
old_state: "{{ (trigger.event.data.old_state.state if trigger.event.data.old_state is not none else '') | lower }}"
new_state: "{{ (trigger.event.data.new_state.state if trigger.event.data.new_state is not none else '') | lower }}"
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
is_monitored_container_event: >-
{% set ent = entity_id %}
{% if ent.startswith('switch.') and (ent.endswith('_container') or ent.endswith('_container_2')) %}
{{ ent in monitored_switches }}
{% elif ent.startswith('binary_sensor.') and (ent.endswith('_status') or ent.endswith('_status_2')) %}
{% set key = ent | replace('binary_sensor.', '') | regex_replace('_status(?:_2)?$', '') %}
{{ ('switch.' ~ key ~ '_container') in monitored_switches or ('switch.' ~ key ~ '_container_2') in monitored_switches }}
{% elif ent.startswith('sensor.') and (ent.endswith('_state') or ent.endswith('_state_2')) %}
{% set key = ent | replace('sensor.', '') | regex_replace('_state(?:_2)?$', '') %}
{{ ('switch.' ~ key ~ '_container') in monitored_switches or ('switch.' ~ key ~ '_container_2') in monitored_switches }}
{% else %}
false
{% endif %}
is_monitored_stack_event: >-
{% set ent = entity_id %}
{% if ent.startswith('binary_sensor.') and ent.endswith('_stack_status') %}
{% set stack_key = ent | replace('binary_sensor.', '') | regex_replace('_stack_status$', '') %}
{{ expand('sensor.' ~ stack_key ~ '_stack_containers_count') | count > 0 }}
{% else %}
false
{% endif %}
condition:
- condition: template
value_template: "{{ trigger.event.data.old_state is not none and trigger.event.data.new_state is not none }}"
- condition: template
value_template: "{{ trigger.event.data.old_state.state != trigger.event.data.new_state.state }}"
value_template: "{{ old_state != new_state }}"
- condition: template
value_template: "{{ is_monitored_container_event or is_monitored_stack_event }}"
action:
- variables:
entity_id: "{{ trigger.event.data.entity_id | default('') }}"
old_state: "{{ trigger.event.data.old_state.state | lower }}"
new_state: "{{ trigger.event.data.new_state.state | lower }}"
monitored: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) }}"
- delay:
seconds: 2
- condition: template
value_template: "{{ states(entity_id) | lower == new_state }}"
- choose:
- conditions:
- condition: template
value_template: >-
{% set ent = entity_id %}
{% if ent.startswith('switch.') and (ent.endswith('_container') or ent.endswith('_container_2')) %}
{{ ent in monitored }}
{% elif ent.startswith('binary_sensor.') and (ent.endswith('_status') or ent.endswith('_status_2')) %}
{% set key = ent | replace('binary_sensor.', '') | regex_replace('_status(?:_2)?$', '') %}
{{ ('switch.' ~ key ~ '_container') in monitored or ('switch.' ~ key ~ '_container_2') in monitored }}
{% elif ent.startswith('sensor.') and (ent.endswith('_state') or ent.endswith('_state_2')) %}
{% set key = ent | replace('sensor.', '') | regex_replace('_state(?:_2)?$', '') %}
{{ ('switch.' ~ key ~ '_container') in monitored or ('switch.' ~ key ~ '_container_2') in monitored }}
{% else %}
false
{% endif %}
value_template: "{{ is_monitored_container_event }}"
sequence:
- variables:
down_states: ['off', 'stopped', 'exited', 'dead', 'unknown', 'unavailable']
@ -748,14 +858,7 @@ automation:
operation: "clear"
- conditions:
- condition: template
value_template: >-
{% set ent = entity_id %}
{% if ent.startswith('binary_sensor.') and ent.endswith('_stack_status') %}
{% set stack_key = ent | replace('binary_sensor.', '') | regex_replace('_stack_status$', '') %}
{{ expand('sensor.' ~ stack_key ~ '_stack_containers_count') | count > 0 }}
{% else %}
false
{% endif %}
value_template: "{{ is_monitored_stack_event }}"
sequence:
- variables:
down_states: ['off', 'unknown', 'unavailable']
@ -785,7 +888,7 @@ automation:
minutes: "/55"
action:
- variables:
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) }}"
monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) | list }}"
- repeat:
for_each: "{{ monitored_switches }}"
sequence:
@ -805,7 +908,7 @@ automation:
{% endif %}
{% endif %}
{% endfor %}
{{ ns.items }}
{{ ns.items | list }}
- repeat:
for_each: "{{ stack_status_entities }}"
sequence:
@ -832,7 +935,9 @@ automation:
data:
title: "Docker Maintenance Check"
value1: "{{ states('sensor.docker_containers_down_count') }} containers are currently down."
value2: "Down: {{ states('sensor.docker_containers_down_list') }}"
value2: >-
{% set down_items = state_attr('sensor.docker_containers_down_list', 'down_containers') | default([], true) | list %}
Down: {{ down_items | join(', ') if (down_items | count > 0) else 'none' }}
who: "carlo"
group: "maintenance"
title1: "Yes, snooze 1h"
@ -846,7 +951,8 @@ automation:
topic: "DOCKER"
message: >-
Maintenance prompt sent to Carlo ({{ states('sensor.docker_containers_down_count') }} down:
{{ states('sensor.docker_containers_down_list') }}).
{% set down_items = state_attr('sensor.docker_containers_down_list', 'down_containers') | default([], true) | list %}
{{ down_items | join(', ') if (down_items | count > 0) else 'none' }}).
- alias: "Docker Maintenance Snooze 1H"
id: docker_maintenance_snooze_1h
@ -885,7 +991,9 @@ automation:
topic: "DOCKER"
message: >-
Maintenance snooze declined with {{ states('sensor.docker_containers_down_count') }}
containers down ({{ states('sensor.docker_containers_down_list') }}).
containers down (
{% set down_items = state_attr('sensor.docker_containers_down_list', 'down_containers') | default([], true) | list %}
{{ down_items | join(', ') if (down_items | count > 0) else 'none' }}).
- alias: "Docker Telemetry Template Refresh"
id: docker_telemetry_template_refresh

@ -11,7 +11,7 @@
# Notes: Only explicit last_status='error' is treated as failure; unknown/unavailable are neutral.
# Notes: HA->Joanna request includes trigger context so Telegram progress messages can identify origin.
# Notes: Creates/clears a Spook Repair issue and requests Joanna remediation on failures.
# Notes: Schedules daily OneNote duplicate-delete maintenance request via Joanna.
# Notes: Daily Joanna recap should be plain-English; only surface detailed index metrics when something materially changes or fails.
######################################################################
sensor:
@ -138,7 +138,9 @@ automation:
diagnostics: "scheduled_time=03:15:00"
request: >-
Run OneNote duplicate cleanup and apply deletions.
Include delete reconciliation and report pages/chunks and delete metrics before and after completion.
Include delete reconciliation, then send a brief conversational morning summary.
Tell me whether cleanup finished and whether anything actually changed.
Only include detailed counts or backend metrics if pages were removed, something failed, or you need my attention.
- id: onenote_indexer_failure_open_repair
alias: OneNote Indexer - Open Repair On Failure

@ -1,10 +1,12 @@
#-------------------------------------------
# @CCOSTAN
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# Space - ISS and space-related sensors.
#-------------------------------------------
######################################################################
## Track space events and ISS passes.
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Space - ISS and launch-related telemetry
# Tracks SpaceX timing and announces an upcoming launch window.
# -------------------------------------------------------------------
# Notes: Guard template triggers against unavailable startup sensor state.
######################################################################
sensor:
@ -32,11 +34,16 @@ automation:
id: 1d42fc4f-a37d-4283-b64b-09242a145598
trigger:
- platform: template
value_template: '{{ (now().strftime("%s") | int + 600) == (states.sensor.spacex.state | int) }}'
value_template: >-
{% set launch_ts = states('sensor.spacex') | int(0) %}
{% set seconds_until = launch_ts - (now().timestamp() | int) %}
{{ launch_ts > 0 and seconds_until > 0 and seconds_until <= 600 }}
action:
- service: script.notify_engine
data:
value1: 'Go Outside! There is a Rocket Launch very soon! {{states.sensor.next_launch.attributes.stream }}'
value1: >-
{% set mission = state_attr('sensor.spacex', 'mission_name') | default('A SpaceX launch', true) %}
Go outside. {{ mission }} is scheduled to launch in about 10 minutes.
group: 'information'

@ -14,7 +14,8 @@ command_line:
- sensor:
name: 'Lines of Code'
unique_id: lines_of_code
command: "find /config -name '*.yaml' | xargs cat | wc -l"
command: "find /config -type f -name '*.yaml' -exec wc -l {} + | awk 'END {print $1}'"
command_timeout: 60
scan_interval: 20000
value_template: "{{ value | int }}"
unit_of_measurement: "count"

@ -48,6 +48,7 @@ Current automations that kick off automated resolutions (via `script.joanna_disp
| `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) |
| `docker_state_sync_repairs_dynamic` | Docker State Sync - Repairs (Dynamic) | [../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) |
### Tips

Loading…
Cancel
Save

Powered by TurnKey Linux.