diff --git a/config/lovelace/infrastructure_apt_updates_card.yaml b/config/lovelace/infrastructure_apt_updates_card.yaml new file mode 100644 index 00000000..3af5abb7 --- /dev/null +++ b/config/lovelace/infrastructure_apt_updates_card.yaml @@ -0,0 +1,35 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Infrastructure Card - APT update status for Docker hosts +# Paste this card into the existing Infrastructure dashboard (storage mode). +# ------------------------------------------------------------------- +# Notes: Depends on `config/packages/apt_updates.yaml`. +###################################################################### + +type: entities +title: "APT Updates (Docker Hosts)" +show_header_toggle: false +entities: + - type: section + label: docker_10 + - entity: sensor.docker_10_apt_status + - entity: sensor.docker_10_apt_last_check + - entity: sensor.docker_10_apt_last_update + - entity: sensor.docker_10_apt_reboot_status + + - type: section + label: docker_14 + - entity: sensor.docker_14_apt_status + - entity: sensor.docker_14_apt_last_check + - entity: sensor.docker_14_apt_last_update + - entity: sensor.docker_14_apt_reboot_status + + - type: section + label: docker_69 + - entity: sensor.docker_69_apt_status + - entity: sensor.docker_69_apt_last_check + - entity: sensor.docker_69_apt_last_update + - entity: sensor.docker_69_apt_reboot_status diff --git a/config/lovelace/infrastructure_apt_updates_view.yaml b/config/lovelace/infrastructure_apt_updates_view.yaml new file mode 100644 index 00000000..c967e204 --- /dev/null +++ b/config/lovelace/infrastructure_apt_updates_view.yaml @@ -0,0 +1,46 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Infrastructure View - APT updates (3-column layout) +# Paste this view into the Infrastructure dashboard (storage mode). +# ------------------------------------------------------------------- +# Notes: Uses `sensor.docker_*` entities from `config/packages/apt_updates.yaml`. +###################################################################### + +title: "Infrastructure - APT Updates" +path: infrastructure-apt-updates +icon: mdi:package-up +type: sections +max_columns: 3 +sections: + - type: grid + columns: 3 + cards: + - type: entities + title: docker_10 + show_header_toggle: false + entities: + - entity: sensor.docker_10_apt_status + - entity: sensor.docker_10_apt_last_check + - entity: sensor.docker_10_apt_last_update + - entity: sensor.docker_10_apt_reboot_status + + - type: entities + title: docker_14 + show_header_toggle: false + entities: + - entity: sensor.docker_14_apt_status + - entity: sensor.docker_14_apt_last_check + - entity: sensor.docker_14_apt_last_update + - entity: sensor.docker_14_apt_reboot_status + + - type: entities + title: docker_69 + show_header_toggle: false + entities: + - entity: sensor.docker_69_apt_status + - entity: sensor.docker_69_apt_last_check + - entity: sensor.docker_69_apt_last_update + - entity: sensor.docker_69_apt_reboot_status diff --git a/config/packages/README.md b/config/packages/README.md index 854aa790..4653b4f2 100755 --- a/config/packages/README.md +++ b/config/packages/README.md @@ -45,6 +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` | +| [apt_updates.yaml](apt_updates.yaml) | Weekly APT patch reporting for Docker hosts + Repairs reboot alerts. | `sensor.docker_*_apt_status`, `repairs.create`, `script.send_to_logbook` | | [phynplus.yaml](phynplus.yaml) | Phyn shutoff automations with push + Activity feed + Repairs issues for leak events. | `valve.phyn_shutoff_valve`, `binary_sensor.phyn_leak_test_running`, `repairs.create` | | [powerwall.yaml](powerwall.yaml) | Track Tesla Powerwall grid status and shed loads automatically when off-grid (alerts include Activity feed + Repairs). | `binary_sensor.powerwall_grid_status`, `sensor.powerwall_*`, `repairs.create` | | [vacuum.yaml](vacuum.yaml) | Dreame vacuum orchestration with room tracking, push alerts, Activity feed, and Repairs issues on errors. | `input_select.l10s_vacuum_phase`, `sensor.l10s_vacuum_error`, `repairs.create` | diff --git a/config/packages/apt_updates.yaml b/config/packages/apt_updates.yaml new file mode 100644 index 00000000..17da1ef7 --- /dev/null +++ b/config/packages/apt_updates.yaml @@ -0,0 +1,369 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# APT Updates - Weekly patch reporting for Docker hosts +# Receives webhook payloads from docker_10/docker_14/docker_69 after APT runs. +# ------------------------------------------------------------------- +# Notes: Hosts run a weekly Wednesday 12:00 APT job and POST JSON to these webhooks. +# Notes: Logbook entry only when `updated: true`; creates Repairs issue if reboot needed. +###################################################################### + +input_datetime: + apt_docker_10_last_check: + name: "docker_10 APT last check" + has_date: true + has_time: true + apt_docker_10_last_update: + name: "docker_10 APT last update" + has_date: true + has_time: true + apt_docker_14_last_check: + name: "docker_14 APT last check" + has_date: true + has_time: true + apt_docker_14_last_update: + name: "docker_14 APT last update" + has_date: true + has_time: true + apt_docker_69_last_check: + name: "docker_69 APT last check" + has_date: true + has_time: true + apt_docker_69_last_update: + name: "docker_69 APT last update" + has_date: true + has_time: true + +input_text: + apt_docker_10_last_result: + name: "docker_10 APT last result" + max: 255 + apt_docker_14_last_result: + name: "docker_14 APT last result" + max: 255 + apt_docker_69_last_result: + name: "docker_69 APT last result" + max: 255 + +input_boolean: + apt_docker_10_reboot_required: + name: "docker_10 reboot required" + icon: mdi:restart-alert + apt_docker_14_reboot_required: + name: "docker_14 reboot required" + icon: mdi:restart-alert + apt_docker_69_reboot_required: + name: "docker_69 reboot required" + icon: mdi:restart-alert + +template: + - sensor: + - name: "docker_10 APT status" + unique_id: apt_docker_10_status + icon: mdi:package-up + state: "{{ states('input_text.apt_docker_10_last_result') }}" + - name: "docker_10 APT last check" + unique_id: apt_docker_10_last_check + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_10_last_check') }}" + - name: "docker_10 APT last update" + unique_id: apt_docker_10_last_update + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_10_last_update') }}" + - name: "docker_10 APT reboot status" + unique_id: apt_docker_10_reboot_status + icon: >- + {{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_10_reboot_required', 'on') + else 'mdi:check-circle' }} + state: >- + {{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_10_reboot_required', 'on') + else 'NO REBOOT NEEDED' }} + - name: "docker_14 APT status" + unique_id: apt_docker_14_status + icon: mdi:package-up + state: "{{ states('input_text.apt_docker_14_last_result') }}" + - name: "docker_14 APT last check" + unique_id: apt_docker_14_last_check + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_14_last_check') }}" + - name: "docker_14 APT last update" + unique_id: apt_docker_14_last_update + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_14_last_update') }}" + - name: "docker_14 APT reboot status" + unique_id: apt_docker_14_reboot_status + icon: >- + {{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_14_reboot_required', 'on') + else 'mdi:check-circle' }} + state: >- + {{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_14_reboot_required', 'on') + else 'NO REBOOT NEEDED' }} + - name: "docker_69 APT status" + unique_id: apt_docker_69_status + icon: mdi:package-up + state: "{{ states('input_text.apt_docker_69_last_result') }}" + - name: "docker_69 APT last check" + unique_id: apt_docker_69_last_check + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_69_last_check') }}" + - name: "docker_69 APT last update" + unique_id: apt_docker_69_last_update + device_class: timestamp + state: "{{ states('input_datetime.apt_docker_69_last_update') }}" + - name: "docker_69 APT reboot status" + unique_id: apt_docker_69_reboot_status + icon: >- + {{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_69_reboot_required', 'on') + else 'mdi:check-circle' }} + state: >- + {{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_69_reboot_required', 'on') + else 'NO REBOOT NEEDED' }} + + - binary_sensor: + - name: "docker_10 APT reboot required" + unique_id: apt_docker_10_reboot_required + device_class: problem + icon: mdi:restart-alert + state: "{{ is_state('input_boolean.apt_docker_10_reboot_required', 'on') }}" + - name: "docker_14 APT reboot required" + unique_id: apt_docker_14_reboot_required + device_class: problem + icon: mdi:restart-alert + state: "{{ is_state('input_boolean.apt_docker_14_reboot_required', 'on') }}" + - name: "docker_69 APT reboot required" + unique_id: apt_docker_69_reboot_required + device_class: problem + icon: mdi:restart-alert + state: "{{ is_state('input_boolean.apt_docker_69_reboot_required', 'on') }}" + +automation: + - alias: "APT Update Report - docker_10" + id: apt_update_report_docker_10 + description: "Receive docker_10 APT results and update helpers/logbook." + mode: queued + trigger: + - platform: webhook + webhook_id: !secret apt_webhook_docker_10 + allowed_methods: + - POST + local_only: true + variables: + payload: "{{ trigger.json | default({}) }}" + success: "{{ payload.get('success', true) | bool }}" + updated: "{{ payload.get('updated', false) | bool }}" + packages: "{{ payload.get('packages', 0) | int(0) }}" + reboot_required: "{{ payload.get('reboot_required', false) | bool }}" + message: "{{ payload.get('message', '') | string }}" + result: >- + {% if not success %} + ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %} + {% elif updated %} + UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %} + {% else %} + NO UPDATES + {% endif %} + log_message: >- + docker_10 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}. + action: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_10_last_check + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: input_text.set_value + target: + entity_id: input_text.apt_docker_10_last_result + data: + value: "{{ result }}" + - choose: + - conditions: "{{ success and reboot_required }}" + sequence: + - service: input_boolean.turn_on + target: + entity_id: input_boolean.apt_docker_10_reboot_required + default: + - service: input_boolean.turn_off + target: + entity_id: input_boolean.apt_docker_10_reboot_required + - choose: + - conditions: "{{ success and updated }}" + sequence: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_10_last_update + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: script.send_to_logbook + data: + topic: "APT" + message: "{{ log_message }}" + + - alias: "APT Update Report - docker_14" + id: apt_update_report_docker_14 + description: "Receive docker_14 APT results and update helpers/logbook." + mode: queued + trigger: + - platform: webhook + webhook_id: !secret apt_webhook_docker_14 + allowed_methods: + - POST + local_only: true + variables: + payload: "{{ trigger.json | default({}) }}" + success: "{{ payload.get('success', true) | bool }}" + updated: "{{ payload.get('updated', false) | bool }}" + packages: "{{ payload.get('packages', 0) | int(0) }}" + reboot_required: "{{ payload.get('reboot_required', false) | bool }}" + message: "{{ payload.get('message', '') | string }}" + result: >- + {% if not success %} + ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %} + {% elif updated %} + UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %} + {% else %} + NO UPDATES + {% endif %} + log_message: >- + docker_14 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}. + action: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_14_last_check + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: input_text.set_value + target: + entity_id: input_text.apt_docker_14_last_result + data: + value: "{{ result }}" + - choose: + - conditions: "{{ success and reboot_required }}" + sequence: + - service: input_boolean.turn_on + target: + entity_id: input_boolean.apt_docker_14_reboot_required + default: + - service: input_boolean.turn_off + target: + entity_id: input_boolean.apt_docker_14_reboot_required + - choose: + - conditions: "{{ success and updated }}" + sequence: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_14_last_update + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: script.send_to_logbook + data: + topic: "APT" + message: "{{ log_message }}" + + - alias: "APT Update Report - docker_69" + id: apt_update_report_docker_69 + description: "Receive docker_69 APT results and update helpers/logbook." + mode: queued + trigger: + - platform: webhook + webhook_id: !secret apt_webhook_docker_69 + allowed_methods: + - POST + local_only: true + variables: + payload: "{{ trigger.json | default({}) }}" + success: "{{ payload.get('success', true) | bool }}" + updated: "{{ payload.get('updated', false) | bool }}" + packages: "{{ payload.get('packages', 0) | int(0) }}" + reboot_required: "{{ payload.get('reboot_required', false) | bool }}" + message: "{{ payload.get('message', '') | string }}" + result: >- + {% if not success %} + ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %} + {% elif updated %} + UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %} + {% else %} + NO UPDATES + {% endif %} + log_message: >- + docker_69 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}. + action: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_69_last_check + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: input_text.set_value + target: + entity_id: input_text.apt_docker_69_last_result + data: + value: "{{ result }}" + - choose: + - conditions: "{{ success and reboot_required }}" + sequence: + - service: input_boolean.turn_on + target: + entity_id: input_boolean.apt_docker_69_reboot_required + default: + - service: input_boolean.turn_off + target: + entity_id: input_boolean.apt_docker_69_reboot_required + - choose: + - conditions: "{{ success and updated }}" + sequence: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.apt_docker_69_last_update + data: + datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}" + - service: script.send_to_logbook + data: + topic: "APT" + message: "{{ log_message }}" + + - alias: "APT Reboot Repairs" + id: apt_reboot_repairs + description: "Create or clear Repairs issues when Docker hosts need a reboot." + mode: queued + trigger: + - platform: state + entity_id: + - binary_sensor.docker_10_apt_reboot_required + - binary_sensor.docker_14_apt_reboot_required + - binary_sensor.docker_69_apt_reboot_required + variables: + host_name: >- + {% if 'docker_10' in trigger.entity_id %} + docker_10 + {% elif 'docker_14' in trigger.entity_id %} + docker_14 + {% else %} + docker_69 + {% endif %} + issue_id: >- + {% if 'docker_10' in trigger.entity_id %} + apt_docker_10_reboot_required + {% elif 'docker_14' in trigger.entity_id %} + apt_docker_14_reboot_required + {% else %} + apt_docker_69_reboot_required + {% endif %} + action: + - choose: + - conditions: "{{ trigger.to_state.state == 'on' }}" + sequence: + - service: repairs.create + data: + issue_id: "{{ issue_id }}" + severity: warning + persistent: true + title: "{{ host_name }} needs reboot" + description: >- + {{ host_name }} reports a reboot is required after APT updates. + Status: {{ states('sensor.' ~ host_name ~ '_apt_status') }}. + default: + - continue_on_error: true + service: repairs.remove + data: + issue_id: "{{ issue_id }}" diff --git a/config/packages/vacuum.yaml b/config/packages/vacuum.yaml index 7966bf42..adedecbd 100755 --- a/config/packages/vacuum.yaml +++ b/config/packages/vacuum.yaml @@ -154,7 +154,7 @@ automation: - condition: template value_template: > {% set last = state_attr('script.l10s_vacuum_start_next_room','last_triggered') %} - {{ last is none or (now() - last).days >= 3 }} + {{ last is none or (now() - last).days >= 4 }} action: - service: input_boolean.turn_on target: diff --git a/config/shell_scripts/apt_weekly.service.sample b/config/shell_scripts/apt_weekly.service.sample new file mode 100644 index 00000000..69f024dc --- /dev/null +++ b/config/shell_scripts/apt_weekly.service.sample @@ -0,0 +1,16 @@ +[Unit] +Description=Weekly APT maintenance (Home Assistant webhook) +After=network-online.target +Wants=network-online.target + +[Service] +Type=oneshot +ExecStart=/usr/local/sbin/apt_weekly.sh https://HOMEASSISTANT_HOST:8123/api/webhook/REPLACE_WITH_WEBHOOK_ID +User=root +Group=root +Nice=10 +IOSchedulingClass=best-effort +IOSchedulingPriority=7 + +[Install] +WantedBy=multi-user.target diff --git a/config/shell_scripts/apt_weekly.sh b/config/shell_scripts/apt_weekly.sh new file mode 100644 index 00000000..16bac9a3 --- /dev/null +++ b/config/shell_scripts/apt_weekly.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash +set -euo pipefail + +# Weekly APT maintenance for docker hosts (runs Wednesdays at 12:00 local via systemd timer) +# Posts results to Home Assistant webhook. + +WEBHOOK_URL="$1" + +if [[ -z "$WEBHOOK_URL" ]]; then + echo "Usage: $0 " >&2 + exit 1 +fi + +log() { echo "[$(date --iso-8601=seconds)] $*"; } + +UPDATED=false +REBOOT=false +MESSAGE="" +log "Updating package lists" +if ! apt-get update -qq; then + MESSAGE="apt-get update failed" + curl -sS -X POST -H 'Content-Type: application/json' -d "{\"success\":false,\"updated\":false,\"packages\":0,\"reboot_required\":false,\"message\":\"$MESSAGE\"}" "$WEBHOOK_URL" + exit 0 +fi + +PACKAGES=$(apt list --upgradable 2>/dev/null | tail -n +2 | wc -l) + +if [[ "$PACKAGES" -gt 0 ]]; then + log "Applying upgrades ($PACKAGES pending)" + if apt-get -y upgrade --with-new-pkgs; then + UPDATED=true + else + MESSAGE="apt-get upgrade failed" + fi +else + log "No packages to upgrade" +fi + +log "Autoremoving stale packages" +apt-get -y autoremove >/dev/null 2>&1 || true + +if [[ -f /var/run/reboot-required ]]; then + REBOOT=true +fi + +payload=$(cat <