diff --git a/.gitignore b/.gitignore index d11adc15..9e6eb1db 100755 --- a/.gitignore +++ b/.gitignore @@ -56,6 +56,7 @@ service_account.json *.cookies AGENTS.md AGENTS.override.md +docs/agent_ops_baselines.md # Directories llmvision diff --git a/codex_skills/infrastructure-doc-sync/SKILL.md b/codex_skills/infrastructure-doc-sync/SKILL.md index 3fb8d788..f0f4bf63 100644 --- a/codex_skills/infrastructure-doc-sync/SKILL.md +++ b/codex_skills/infrastructure-doc-sync/SKILL.md @@ -1,11 +1,12 @@ --- name: infrastructure-doc-sync -description: "Use when infra or container placement changes were made in a session and documentation must be synchronized across AGENTS.md, README(s), and Infra Info snapshot (`overview.json`). Covers host maps, role notes, and safe high-level summary updates." +description: "Use when infra/container placement changes require synchronized AGENTS, docs, and Infra Info updates while keeping AGENTS concise, scoped, and non-runbook." --- # Infrastructure Doc Sync Keep infrastructure documentation aligned after operational changes. +Keep `AGENTS.md` short and task-scoped; move long runbooks to dedicated docs. ## When to Use @@ -15,29 +16,46 @@ Keep infrastructure documentation aligned after operational changes. - Any update to an `AGENTS.md` file that affects infra context. - Any user-facing shortcut URL changed (Dashy). -## Required Update Targets +## Required Update Targets (As Applicable) -1. `../AGENTS.md` (workspace-level infra truth in `h:\hass\docker_files\AGENTS.md`) -2. Relevant repo README(s), usually: +1. Workspace infra truth: + - `../AGENTS.md` (workspace-level host/topology source) +2. Repo-scoped `AGENTS.md` files touched by the change: + - Keep only hard constraints and applicability gates. + - Remove/move long runbooks into dedicated docs. +3. Relevant repo docs: - `README.md` - `codex_skills/README.md` (if adding/updating skills) -3. Dashy shortcuts (if any service URL/host changed): +4. Ops/runbook doc (if AGENTS runbook content is reduced): + - Example: `docs/agent_ops_baselines.md` (repo-local operational baseline) +5. Dashy shortcuts (if any service URL/host changed): - `h:\hass\docker_files\dashy/conf.yml` - Reload Dashy on docker_17 after edits: `ssh hass@192.168.10.17 "cd ~/docker_files && docker compose up -d dashy"` -4. Infra Info snapshot JSON: +6. Infra Info snapshot JSON: - `docker_69:/home/hass/docker_files/infra_info/data/overview.json` ## Workflow 1. Collect current runtime truth from hosts (containers, network mode, ports, and workload role). -2. Update AGENTS host maps and operational notes first. -3. If end-user entry points changed, update Dashy shortcuts (`dashy/conf.yml`) to match the new reality. -4. Update README sections impacted by the change (short, factual, no drift). -5. Update `overview.json` to mirror the same outcome at a high level. -6. Validate: +2. Update workspace `AGENTS.md` first for host/topology truth. +3. Update repo-level `AGENTS.md` with concise, scoped constraints only. +4. Move long operational/runbook details out of `AGENTS.md` into a dedicated doc when needed. +5. If end-user entry points changed, update Dashy shortcuts (`dashy/conf.yml`) to match reality. +6. Update README/skill docs impacted by the change (short, factual, no drift). +7. Update `overview.json` to mirror the same outcome at a high level. +8. Validate: - JSON is valid (`python -m json.tool` equivalent). - Dashy `conf.yml` references the intended hostname(s)/ports (no stale LAN IPs unless intentionally required). - AGENTS and README statements do not conflict with runtime. + - Repo-level AGENTS do not contain long runbooks duplicated from dedicated docs. + +## AGENTS Quality Rules + +- Prefer short checklists over narrative paragraphs. +- Keep only non-discoverable, high-impact constraints in AGENTS. +- Add explicit applicability gates where a file has mixed-scope guidance. +- Keep specialized/deeper-scoped AGENTS concise and task-specific. +- De-duplicate repeated policy lines across global/workspace/repo scopes. ## Infra Info Content Rules @@ -60,5 +78,6 @@ Always report: - What changed (files + purpose). - Final intended topology/placement. - Any Dashy shortcuts touched (or explicitly state "no Dashy updates needed"). +- Whether runbook content was moved from AGENTS into a dedicated ops doc. - Any unresolved follow-up items. diff --git a/codex_skills/infrastructure-doc-sync/agents/openai.yaml b/codex_skills/infrastructure-doc-sync/agents/openai.yaml index 4cf1f9f5..2029468b 100644 --- a/codex_skills/infrastructure-doc-sync/agents/openai.yaml +++ b/codex_skills/infrastructure-doc-sync/agents/openai.yaml @@ -1,4 +1,4 @@ interface: display_name: "Infrastructure Doc Sync" - short_description: "Sync AGENTS/README/overview after infra changes" - default_prompt: "Use $infrastructure-doc-sync to update AGENTS.md, Dashy shortcuts (if any URLs changed), README notes, and infra_info overview.json after Docker/host topology changes." + short_description: "Sync infra docs with concise AGENTS" + default_prompt: "Use $infrastructure-doc-sync to keep AGENTS concise and scoped, move runbook content into dedicated docs when needed, and sync README/Dashy/infra_info overview.json after infra changes." diff --git a/config/dashboards/infrastructure/partials/docker_container_restart_include.yaml b/config/dashboards/infrastructure/partials/docker_container_restart_include.yaml deleted file mode 100644 index b14eb96d..00000000 --- a/config/dashboards/infrastructure/partials/docker_container_restart_include.yaml +++ /dev/null @@ -1,441 +0,0 @@ -###################################################################### -# @CCOSTAN - Follow Me on X -# For more info visit https://www.vcloudinfo.com/click-here -# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig -# ------------------------------------------------------------------- -# Infrastructure Include - Docker container restart buttons -# Auto-entities include list for Portainer container restart buttons (button.*_restart_container). -# ------------------------------------------------------------------- -# Notes: Generated from core.entity_registry (platform: portainer). -###################################################################### - -- entity_id: button.cloudflared_kch_restart_container - options: - type: tile - name: cloudflared_kch - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.cloudflared_kch_restart_container -- entity_id: button.cloudflared_wp_restart_container - options: - type: tile - name: cloudflared_wp - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.cloudflared_wp_restart_container -- entity_id: button.codex_appliance_restart_container - options: - type: tile - name: codex_appliance - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.codex_appliance_restart_container -- entity_id: button.college_budget_app_restart_container - options: - type: tile - name: college_budget_app - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.college_budget_app_restart_container -- entity_id: button.cruise_tracker_restart_container - options: - type: tile - name: cruise_tracker - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.cruise_tracker_restart_container -- entity_id: button.dashy_restart_container - options: - type: tile - name: dashy - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.dashy_restart_container -- entity_id: button.dozzle_agent_10_restart_container - options: - type: tile - name: dozzle_agent_10 - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.dozzle_agent_10_restart_container -- entity_id: button.dozzle_agent_14_restart_container - options: - type: tile - name: dozzle_agent_14 - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.dozzle_agent_14_restart_container -- entity_id: button.dozzle_restart_container - options: - type: tile - name: dozzle - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.dozzle_restart_container -- entity_id: button.duplicati_restart_container - options: - type: tile - name: duplicati - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.duplicati_restart_container -- entity_id: button.esphome_restart_container - options: - type: tile - name: esphome - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.esphome_restart_container -- entity_id: button.foodie_tracker_restart_container - options: - type: tile - name: foodie_tracker - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.foodie_tracker_restart_container -- entity_id: button.frigate_restart_container - options: - type: tile - name: frigate - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.frigate_restart_container -- entity_id: button.games_hub_restart_container - options: - type: tile - name: games_hub - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.games_hub_restart_container -- entity_id: button.home_assistant_restart_container - options: - type: tile - name: home-assistant - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.home_assistant_restart_container -- entity_id: button.imposter_restart_container - options: - type: tile - name: imposter - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.imposter_restart_container -- entity_id: button.infra_info_restart_container - options: - type: tile - name: infra_info - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.infra_info_restart_container -- entity_id: button.kingcrafthomes_restart_container - options: - type: tile - name: kingcrafthomes - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.kingcrafthomes_restart_container -- entity_id: button.mariadb_backup_restart_container - options: - type: tile - name: mariadb-backup - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.mariadb_backup_restart_container -- entity_id: button.mariadb_restart_container - options: - type: tile - name: mariadb - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.mariadb_restart_container -- entity_id: button.matter_server_restart_container - options: - type: tile - name: matter-server - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.matter_server_restart_container -- entity_id: button.mqtt_restart_container - options: - type: tile - name: mqtt - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.mqtt_restart_container -- entity_id: button.nebula_sync_restart_container - options: - type: tile - name: nebula_sync - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.nebula_sync_restart_container -- entity_id: button.panel_notes_restart_container - options: - type: tile - name: panel_notes - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.panel_notes_restart_container -- entity_id: button.pihole_restart_container - options: - type: tile - name: pihole - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.pihole_restart_container -- entity_id: button.pihole_secondary_restart_container - options: - type: tile - name: pihole_secondary - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.pihole_secondary_restart_container -- entity_id: button.poker_tracker_restart_container - options: - type: tile - name: poker_tracker - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.poker_tracker_restart_container -- entity_id: button.portainer_agent_restart_container - options: - type: tile - name: portainer_agent - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.portainer_agent_restart_container -- entity_id: button.portainer_restart_container - options: - type: tile - name: portainer - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.portainer_restart_container -- entity_id: button.postgres_webhooks_engine_restart_container - options: - type: tile - name: postgres_webhooks_engine - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.postgres_webhooks_engine_restart_container -- entity_id: button.rc_price_checker_restart_container - options: - type: tile - name: rc_price_checker - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.rc_price_checker_restart_container -- entity_id: button.redis_webhooks_engine_restart_container - options: - type: tile - name: redis_webhooks_engine - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.redis_webhooks_engine_restart_container -- entity_id: button.rvtools_ppt_web_restart_container - options: - type: tile - name: rvtools_ppt_web - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.rvtools_ppt_web_restart_container -- entity_id: button.steelesharing_home_restart_container - options: - type: tile - name: steelesharing_home - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.steelesharing_home_restart_container -- entity_id: button.tapple_restart_container - options: - type: tile - name: tapple - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.tapple_restart_container -- entity_id: button.tugtainer_agent_restart_container - options: - type: tile - name: tugtainer-agent - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.tugtainer_agent_restart_container -- entity_id: button.tugtainer_restart_container - options: - type: tile - name: tugtainer - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.tugtainer_restart_container -- entity_id: button.tugtainer_socket_proxy_restart_container - options: - type: tile - name: tugtainer_socket_proxy - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.tugtainer_socket_proxy_restart_container -- entity_id: button.unifi_restart_container - options: - type: tile - name: unifi - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.unifi_restart_container -- entity_id: button.webhooks_engine_restart_container - options: - type: tile - name: webhooks_engine - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.webhooks_engine_restart_container -- entity_id: button.wordpress_db_restart_container - options: - type: tile - name: wordpress_db - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.wordpress_db_restart_container -- entity_id: button.wordpress_wp_restart_container - options: - type: tile - name: wordpress_wp - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.wordpress_wp_restart_container -- entity_id: button.wyze_bridge_restart_container - options: - type: tile - name: wyze-bridge - hide_state: true - tap_action: - action: call-service - service: button.press - service_data: - entity_id: button.wyze_bridge_restart_container diff --git a/config/dashboards/infrastructure/partials/docker_container_rows_include.yaml b/config/dashboards/infrastructure/partials/docker_container_rows_include.yaml deleted file mode 100644 index 87d3acdb..00000000 --- a/config/dashboards/infrastructure/partials/docker_container_rows_include.yaml +++ /dev/null @@ -1,416 +0,0 @@ -###################################################################### -# @CCOSTAN - Follow Me on X -# For more info visit https://www.vcloudinfo.com/click-here -# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig -# ------------------------------------------------------------------- -# Infrastructure Include - Docker container rows -# Auto-entities include list mapping Portainer status -> restart button. -# ------------------------------------------------------------------- -# Notes: Generated from core.entity_registry + button restart entities. -###################################################################### - -- entity_id: binary_sensor.cloudflared_kch_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: cloudflared_kch - icon: mdi:docker - variables: - restart_button: button.cloudflared_kch_restart_container - name: cloudflared_kch -- entity_id: binary_sensor.cloudflared_wp_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: cloudflared_wp - icon: mdi:docker - variables: - restart_button: button.cloudflared_wp_restart_container - name: cloudflared_wp -- entity_id: binary_sensor.codex_appliance_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: codex_appliance - icon: mdi:docker - variables: - restart_button: button.codex_appliance_restart_container - name: codex_appliance -- entity_id: binary_sensor.college_budget_app_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: college_budget_app - icon: mdi:docker - variables: - restart_button: button.college_budget_app_restart_container - name: college_budget_app -- entity_id: binary_sensor.cruise_tracker_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: cruise_tracker - icon: mdi:docker - variables: - restart_button: button.cruise_tracker_restart_container - name: cruise_tracker -- entity_id: binary_sensor.dashy_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: dashy - icon: mdi:docker - variables: - restart_button: button.dashy_restart_container - name: dashy -- entity_id: binary_sensor.dozzle_agent_10_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: dozzle_agent_10 - icon: mdi:docker - variables: - restart_button: button.dozzle_agent_10_restart_container - name: dozzle_agent_10 -- entity_id: binary_sensor.dozzle_agent_14_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: dozzle_agent_14 - icon: mdi:docker - variables: - restart_button: button.dozzle_agent_14_restart_container - name: dozzle_agent_14 -- entity_id: binary_sensor.dozzle_agent_17_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: dozzle_agent_17 - icon: mdi:docker - variables: - restart_button: button.dozzle_agent_17_restart_container - name: dozzle_agent_17 -- entity_id: binary_sensor.dozzle_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: dozzle - icon: mdi:docker - variables: - restart_button: button.dozzle_restart_container - name: dozzle -- entity_id: binary_sensor.duplicati_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: duplicati - icon: mdi:docker - variables: - restart_button: button.duplicati_restart_container - name: duplicati -- entity_id: binary_sensor.esphome_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: esphome - icon: mdi:docker - variables: - restart_button: button.esphome_restart_container - name: esphome -- entity_id: binary_sensor.fed437a0f191_tugtainer_socket_proxy_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: fed437a0f191_tugtainer_socket_proxy - icon: mdi:docker - variables: - restart_button: button.fed437a0f191_tugtainer_socket_proxy_restart_container - name: fed437a0f191_tugtainer_socket_proxy -- entity_id: binary_sensor.foodie_tracker_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: foodie_tracker - icon: mdi:docker - variables: - restart_button: button.foodie_tracker_restart_container - name: foodie_tracker -- entity_id: binary_sensor.frigate_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: frigate - icon: mdi:docker - variables: - restart_button: button.frigate_restart_container - name: frigate -- entity_id: binary_sensor.games_hub_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: games_hub - icon: mdi:docker - variables: - restart_button: button.games_hub_restart_container - name: games_hub -- entity_id: binary_sensor.home_assistant_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: home-assistant - icon: mdi:docker - variables: - restart_button: button.home_assistant_restart_container - name: home-assistant -- entity_id: binary_sensor.imposter_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: imposter - icon: mdi:docker - variables: - restart_button: button.imposter_restart_container - name: imposter -- entity_id: binary_sensor.infra_info_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: infra_info - icon: mdi:docker - variables: - restart_button: button.infra_info_restart_container - name: infra_info -- entity_id: binary_sensor.kingcrafthomes_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: kingcrafthomes - icon: mdi:docker - variables: - restart_button: button.kingcrafthomes_restart_container - name: kingcrafthomes -- entity_id: binary_sensor.mariadb_backup_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: mariadb-backup - icon: mdi:docker - variables: - restart_button: button.mariadb_backup_restart_container - name: mariadb-backup -- entity_id: binary_sensor.mariadb_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: mariadb - icon: mdi:docker - variables: - restart_button: button.mariadb_restart_container - name: mariadb -- entity_id: binary_sensor.matter_server_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: matter-server - icon: mdi:docker - variables: - restart_button: button.matter_server_restart_container - name: matter-server -- entity_id: binary_sensor.mqtt_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: mqtt - icon: mdi:docker - variables: - restart_button: button.mqtt_restart_container - name: mqtt -- entity_id: binary_sensor.nebula_sync_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: nebula_sync - icon: mdi:docker - variables: - restart_button: button.nebula_sync_restart_container - name: nebula_sync -- entity_id: binary_sensor.panel_notes_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: panel_notes - icon: mdi:docker - variables: - restart_button: button.panel_notes_restart_container - name: panel_notes -- entity_id: binary_sensor.pihole_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: pihole - icon: mdi:docker - variables: - restart_button: button.pihole_restart_container - name: pihole -- entity_id: binary_sensor.pihole_secondary_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: pihole_secondary - icon: mdi:docker - variables: - restart_button: button.pihole_secondary_restart_container - name: pihole_secondary -- entity_id: binary_sensor.poker_tracker_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: poker_tracker - icon: mdi:docker - variables: - restart_button: button.poker_tracker_restart_container - name: poker_tracker -- entity_id: binary_sensor.portainer_agent_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: portainer_agent - icon: mdi:docker - variables: - restart_button: button.portainer_agent_restart_container - name: portainer_agent -- entity_id: binary_sensor.portainer_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: portainer - icon: mdi:docker - variables: - restart_button: button.portainer_restart_container - name: portainer -- entity_id: binary_sensor.postgres_webhooks_engine_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: postgres_webhooks_engine - icon: mdi:docker - variables: - restart_button: button.postgres_webhooks_engine_restart_container - name: postgres_webhooks_engine -- entity_id: binary_sensor.rc_price_checker_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: rc_price_checker - icon: mdi:docker - variables: - restart_button: button.rc_price_checker_restart_container - name: rc_price_checker -- entity_id: binary_sensor.redis_webhooks_engine_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: redis_webhooks_engine - icon: mdi:docker - variables: - restart_button: button.redis_webhooks_engine_restart_container - name: redis_webhooks_engine -- entity_id: binary_sensor.rvtools_ppt_web_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: rvtools_ppt_web - icon: mdi:docker - variables: - restart_button: button.rvtools_ppt_web_restart_container - name: rvtools_ppt_web -- entity_id: binary_sensor.steelesharing_home_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: steelesharing_home - icon: mdi:docker - variables: - restart_button: button.steelesharing_home_restart_container - name: steelesharing_home -- entity_id: binary_sensor.tapple_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: tapple - icon: mdi:docker - variables: - restart_button: button.tapple_restart_container - name: tapple -- entity_id: binary_sensor.tugtainer_agent_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: tugtainer-agent - icon: mdi:docker - variables: - restart_button: button.tugtainer_agent_restart_container - name: tugtainer-agent -- entity_id: binary_sensor.tugtainer_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: tugtainer - icon: mdi:docker - variables: - restart_button: button.tugtainer_restart_container - name: tugtainer -- entity_id: binary_sensor.tugtainer_socket_proxy_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: tugtainer_socket_proxy - icon: mdi:docker - variables: - restart_button: button.tugtainer_socket_proxy_restart_container - name: tugtainer_socket_proxy -- entity_id: binary_sensor.unifi_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: unifi - icon: mdi:docker - variables: - restart_button: button.unifi_restart_container - name: unifi -- entity_id: binary_sensor.webhooks_engine_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: webhooks_engine - icon: mdi:docker - variables: - restart_button: button.webhooks_engine_restart_container - name: webhooks_engine -- entity_id: binary_sensor.wordpress_db_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: wordpress_db - icon: mdi:docker - variables: - restart_button: button.wordpress_db_restart_container - name: wordpress_db -- entity_id: binary_sensor.wordpress_wp_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: wordpress_wp - icon: mdi:docker - variables: - restart_button: button.wordpress_wp_restart_container - name: wordpress_wp -- entity_id: binary_sensor.wyze_bridge_status - options: - type: custom:button-card - template: bearstone_infra_container_row - name: wyze-bridge - icon: mdi:docker - variables: - restart_button: button.wyze_bridge_restart_container - name: wyze-bridge diff --git a/config/dashboards/infrastructure/partials/docker_containers_sections.yaml b/config/dashboards/infrastructure/partials/docker_containers_sections.yaml index b49b770e..7b05e4fa 100644 --- a/config/dashboards/infrastructure/partials/docker_containers_sections.yaml +++ b/config/dashboards/infrastructure/partials/docker_containers_sections.yaml @@ -7,7 +7,7 @@ # Related Issue: 1560 # Sections layout for the Docker containers view. # ------------------------------------------------------------------- -# Notes: Uses Portainer entities (`binary_sensor.*_status`, `button.*_restart_container`). +# Notes: Auto-discovers Portainer container entities from `switch.*_container`. ###################################################################### - type: grid @@ -98,7 +98,12 @@ grid-template-columns: repeat(2, minmax(0, 1fr)) card_param: cards filter: - include: !include /config/dashboards/infrastructure/partials/docker_container_rows_include.yaml + include: + - group: group.docker_monitored_containers + options: + type: custom:button-card + template: bearstone_infra_container_row + icon: mdi:docker exclude: - state: unavailable sort: diff --git a/config/dashboards/infrastructure/partials/home_sections.yaml b/config/dashboards/infrastructure/partials/home_sections.yaml index af60a52d..a5ecec9c 100644 --- a/config/dashboards/infrastructure/partials/home_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_sections.yaml @@ -416,7 +416,12 @@ grid-template-columns: repeat(1, minmax(0, 1fr)) card_param: cards filter: - include: !include /config/dashboards/infrastructure/partials/docker_container_rows_include.yaml + include: + - group: group.docker_monitored_containers + options: + type: custom:button-card + template: bearstone_infra_container_row + icon: mdi:docker exclude: - state: 'on' diff --git a/config/dashboards/infrastructure/templates/button_card_templates.yaml b/config/dashboards/infrastructure/templates/button_card_templates.yaml index 94e35ae7..30d9b9d1 100644 --- a/config/dashboards/infrastructure/templates/button_card_templates.yaml +++ b/config/dashboards/infrastructure/templates/button_card_templates.yaml @@ -179,16 +179,52 @@ bearstone_infra_container_row: action: call-service service: button.press service_data: - entity_id: '[[[ return variables.restart_button ]]]' + entity_id: > + [[[ + if (variables && variables.restart_button) return variables.restart_button; + const ent = (entity && entity.entity_id) ? String(entity.entity_id) : ''; + let key = ''; + if (ent.startsWith('binary_sensor.') && ent.endsWith('_status')) { + key = ent.replace('binary_sensor.', '').replace(/_status$/, ''); + } else if (ent.startsWith('switch.') && ent.endsWith('_container')) { + key = ent.replace('switch.', '').replace(/_container$/, ''); + } + return key ? `button.${key}_restart_container` : ''; + ]]] confirmation: text: '[[[ return "Restart container " + entity.attributes.friendly_name + "?" ]]]' icon: mdi:docker + name: > + [[[ + if (variables && variables.name) return variables.name; + const ent = (entity && entity.entity_id) ? String(entity.entity_id) : ''; + const friendly = (entity && entity.attributes && entity.attributes.friendly_name) + ? String(entity.attributes.friendly_name) + : ''; + let key = ''; + if (ent.startsWith('binary_sensor.') && ent.endsWith('_status')) { + key = ent.replace('binary_sensor.', '').replace(/_status$/, ''); + } else if (ent.startsWith('switch.') && ent.endsWith('_container')) { + key = ent.replace('switch.', '').replace(/_container$/, ''); + } + if (friendly && friendly !== 'Container') { + return friendly.replace(/\s+Container$/, ''); + } + return key || friendly || ent; + ]]] custom_fields: image: > [[[ + const ent = (entity && entity.entity_id) ? String(entity.entity_id) : ''; + let key = ''; + if (ent.startsWith('binary_sensor.') && ent.endsWith('_status')) { + key = ent.replace('binary_sensor.', '').replace(/_status$/, ''); + } else if (ent.startsWith('switch.') && ent.endsWith('_container')) { + key = ent.replace('switch.', '').replace(/_container$/, ''); + } const imageEntity = variables.image_sensor ? variables.image_sensor - : entity.entity_id.replace('binary_sensor.', 'sensor.').replace(/_status$/, '_image'); + : (key ? `sensor.${key}_image` : ''); const imageValue = states[imageEntity]?.state; if (!imageValue || ['unknown', 'unavailable', 'none', ''].includes(String(imageValue).toLowerCase())) { return 'image: n/a'; @@ -238,7 +274,21 @@ bearstone_infra_container_row: - border-radius: 999px - background: rgba(0,0,0,0.06) - color: var(--secondary-text-color) - - line-height: 1.1 + card: + - display: > + [[[ + const ent = (entity && entity.entity_id) ? String(entity.entity_id) : ''; + let key = ''; + if (ent.startsWith('binary_sensor.') && ent.endsWith('_status')) { + key = ent.replace('binary_sensor.', '').replace(/_status$/, ''); + } else if (ent.startsWith('switch.') && ent.endsWith('_container')) { + key = ent.replace('switch.', '').replace(/_container$/, ''); + } + const switchEntity = key ? `switch.${key}_container` : ''; + const monitored = states['group.docker_monitored_containers']?.attributes?.entity_id || []; + const restart = key ? `button.${key}_restart_container` : ''; + return (restart && states[restart] && monitored.includes(switchEntity)) ? 'block' : 'none'; + ]]] state: - value: 'on' styles: diff --git a/config/group/media_players.yaml b/config/group/media_players.yaml index bbbc9159..8fee83ae 100755 --- a/config/group/media_players.yaml +++ b/config/group/media_players.yaml @@ -1,7 +1,18 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Media Players Group - Multi-room media control +# Keep entries aligned with active media_player IDs. +# ------------------------------------------------------------------- +# Related Issue: n/a +# Notes: Removed stale media_player.* refs that triggered Spook warnings. +###################################################################### + media_players: entities: - media_player.livingroomcc - - media_player.living_room_tv - media_player.living_room_ultra - media_player.bedroom_tablet - media_player.living_room @@ -9,7 +20,6 @@ media_players: - media_player.garage - media_player.office - media_player.kitchen - - media_player.justin_room - media_player.upstairs - media_player.stacey_bedroom - media_player.carlo_bedroom @@ -24,4 +34,3 @@ tvs: - media_player.roku_living_room - media_player.tv_samsung_q70_series_65 - diff --git a/config/packages/docker_infrastructure.yaml b/config/packages/docker_infrastructure.yaml index aa93b99c..b5fed89a 100644 --- a/config/packages/docker_infrastructure.yaml +++ b/config/packages/docker_infrastructure.yaml @@ -9,7 +9,7 @@ # Notes: Hosts run weekly Wed 12:00 APT job and POST JSON to webhooks. # 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 watchlist is explicit; extend entity list as needed. +# Notes: Container monitoring is dynamic with binary_sensor status preferred over switch state. ###################################################################### input_datetime: @@ -45,6 +45,10 @@ input_datetime: name: "docker_69 APT last update" has_date: true has_time: true + docker_container_alerts_snooze_until: + name: "Docker container alerts snooze until" + has_date: true + has_time: true input_text: apt_docker_10_last_result: @@ -60,6 +64,58 @@ input_text: name: "docker_69 APT last result" max: 255 +group: + docker_monitored_containers: + name: Docker Monitored Containers + entities: + - switch.cloudflared_kch_container + - switch.cloudflared_wp_container + - switch.codex_appliance_container + - switch.college_budget_app_container + - switch.cruise_tracker_container + - switch.dashy_container + - switch.docker_socket_proxy_container + - switch.dozzle_container + - switch.dozzle_agent_10_container + - switch.dozzle_agent_14_container + - switch.dozzle_agent_17_container + - switch.dozzle_agent_69_container + - switch.duplicati_container + - switch.esphome_container + - switch.fed437a0f191_tugtainer_socket_proxy_container + - switch.foodie_tracker_container + - switch.frigate_container + - switch.games_hub_container + - switch.home_assistant_container + - switch.imposter_container + - switch.infra_info_container + - switch.kingcrafthomes_container + - switch.lmediaservices_container + - switch.mariadb_container + - switch.mariadb_backup_container + - switch.matter_server_container + - switch.mqtt_container + - switch.nebula_sync_container + - switch.panel_notes_container + - switch.pihole_container + - switch.pihole_secondary_container + - switch.poker_tracker_container + - switch.portainer_container + - switch.portainer_agent_container + - switch.postgres_webhooks_engine_container + - switch.rc_price_checker_container + - switch.redis_webhooks_engine_container + - switch.rvtools_ppt_web_container + - switch.tapple_container + - switch.tugtainer_container + - switch.tugtainer_agent_container + - switch.tugtainer_socket_proxy_container + - switch.unifi_container + - switch.webhooks_engine_container + - switch.wordpress_db_container + - switch.wordpress_wp_container + - switch.wyze_bridge_container + template: - sensor: - name: "docker_10 APT status" @@ -143,6 +199,146 @@ template: {{ as_local(as_datetime(stamp)) }} {% endif %} + - sensor: + - name: "Docker Containers Down List" + unique_id: docker_containers_down_list + icon: mdi:docker + state: >- + {% set ns = namespace(keys=[], down=[]) %} + {% set monitored = state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) %} + {% for switch_entity in monitored %} + {% set key = switch_entity | replace('switch.', '') | regex_replace('_container$', '') %} + {% 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 switch_entity = 'switch.' ~ key ~ '_container' %} + {% if expand(status_entity) | count > 0 %} + {% set effective_state = states(status_entity) | lower %} + {% elif expand(switch_entity) | count > 0 %} + {% set effective_state = states(switch_entity) | lower %} + {% else %} + {% set effective_state = 'unknown' %} + {% endif %} + {% if effective_state in ['off', 'unknown', 'unavailable'] %} + {% set ns.down = ns.down + [key] %} + {% endif %} + {% endfor %} + {{ ns.down | sort | join(', ') if (ns.down | count > 0) else 'none' }} + + - 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 %} + + - binary_sensor: + - name: "Docker Container Alerts Snoozed" + unique_id: docker_container_alerts_snoozed + device_class: problem + icon: mdi:bell-sleep + state: >- + {% set stamp = states('input_datetime.docker_container_alerts_snooze_until') %} + {% set until_ts = as_datetime(stamp) %} + {{ until_ts is not none and now() < until_ts }} + +script: + docker_container_repairs_sync: + alias: Docker Container Repairs Sync + mode: parallel + fields: + entity_id: + description: Changed Portainer entity (`switch.*_container` or `binary_sensor.*_status`) + example: "switch.rc_price_checker_container" + operation: + description: "Sync operation: create or clear" + example: "create" + delay_minutes: + description: "Optional delay before evaluation (used for create path)" + example: 5 + sequence: + - variables: + down_states: ['off', 'unknown', 'unavailable'] + src_entity: "{{ entity_id | default('', true) }}" + op: "{{ operation | default('create', true) | lower }}" + wait_minutes: "{{ delay_minutes | default(0) | int(0) }}" + container_key: >- + {% if src_entity.startswith('binary_sensor.') %} + {{ src_entity | replace('binary_sensor.', '') | regex_replace('_status$', '') }} + {% elif src_entity.startswith('switch.') %} + {{ src_entity | replace('switch.', '') | regex_replace('_container$', '') }} + {% else %} + {{ src_entity }} + {% endif %} + switch_entity: "switch.{{ container_key }}_container" + restart_entity: "button.{{ container_key }}_restart_container" + status_entity: "binary_sensor.{{ container_key }}_status" + monitored_switches: "{{ state_attr('group.docker_monitored_containers', 'entity_id') | default([], true) }}" + tracked_container: "{{ switch_entity in monitored_switches }}" + effective_entity: >- + {% if expand(status_entity) | count > 0 %} + {{ status_entity }} + {% elif expand(switch_entity) | count > 0 %} + {{ switch_entity }} + {% else %} + {{ src_entity }} + {% endif %} + issue_id: "docker_container_{{ container_key }}_offline" + - condition: template + value_template: "{{ tracked_container and op in ['create', 'clear'] }}" + - choose: + - conditions: "{{ op == 'create' }}" + sequence: + - choose: + - conditions: "{{ wait_minutes > 0 }}" + sequence: + - delay: + minutes: "{{ wait_minutes }}" + - variables: + effective_state: "{{ states(effective_entity) | lower }}" + container_name: "{{ state_attr(effective_entity, 'friendly_name') | default(container_key, true) }}" + - condition: template + value_template: "{{ effective_state in down_states }}" + - condition: state + entity_id: binary_sensor.docker_container_alerts_snoozed + state: "off" + - service: repairs.create + data: + issue_id: "{{ issue_id }}" + title: "Container offline: {{ container_name }}" + description: >- + {{ container_name }} has been {{ effective_state }} for over 5 minutes. + Effective entity: {{ effective_entity }}. + severity: warning + persistent: true + - service: script.send_to_logbook + data: + topic: "DOCKER" + message: "{{ container_name }} is {{ effective_state }} for over 5 minutes." + - conditions: "{{ op == 'clear' }}" + sequence: + - variables: + effective_state: "{{ states(effective_entity) | lower }}" + container_name: "{{ state_attr(effective_entity, 'friendly_name') | default(container_key, true) }}" + - condition: template + value_template: "{{ effective_state not in down_states }}" + - service: repairs.remove + continue_on_error: true + data: + issue_id: "{{ issue_id }}" + - service: script.send_to_logbook + data: + topic: "DOCKER" + message: "{{ container_name }} recovered ({{ effective_state }})." + automation: - alias: "APT Update Report - Docker Hosts" id: apt_update_report_docker_hosts @@ -238,65 +434,112 @@ automation: topic: "APT" message: "{{ log_message }}" - - alias: "Docker Container Offline - Create Repairs" - id: docker_container_offline_repairs_create - description: "Create a Repairs issue when a watched container is down for 5+ minutes." + - alias: "Docker Container State Sync - Repairs (Dynamic)" + id: docker_container_state_sync_repairs_dynamic + description: "Detect dynamic container state transitions and delegate Repairs sync to script helper." mode: parallel trigger: - - platform: state - entity_id: - - sensor.rc_price_checker_state - - switch.rc_price_checker_container - to: "unavailable" - for: "00:05:00" - - platform: state - entity_id: - - sensor.rc_price_checker_state - to: "unknown" - for: "00:05:00" - - platform: state - entity_id: - - sensor.rc_price_checker_state - to: "restarting" - for: "00:05:00" - - platform: state - entity_id: - - switch.rc_price_checker_container - to: "off" - for: "00:05:00" - variables: - issue_id: "docker_container_{{ trigger.entity_id | replace('.', '_') }}" - container_name: >- - {{ state_attr(trigger.entity_id, 'friendly_name') | default(trigger.entity_id) }} - current_state: "{{ states(trigger.entity_id) }}" + - platform: event + event_type: state_changed + condition: + - condition: template + value_template: >- + {% set ent = trigger.event.data.entity_id | default('') %} + {{ ent.startswith('switch.') and ent.endswith('_container') or + ent.startswith('binary_sensor.') and ent.endswith('_status') }} + - 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 }}" + action: + - variables: + down_states: ['off', 'unknown', 'unavailable'] + entity_id: "{{ trigger.event.data.entity_id }}" + old_state: "{{ trigger.event.data.old_state.state | lower }}" + new_state: "{{ trigger.event.data.new_state.state | lower }}" + - choose: + - conditions: "{{ new_state in down_states and old_state not in down_states }}" + sequence: + - service: script.docker_container_repairs_sync + data: + entity_id: "{{ entity_id }}" + operation: "create" + delay_minutes: 5 + - conditions: "{{ old_state in down_states and new_state not in down_states }}" + sequence: + - service: script.docker_container_repairs_sync + data: + entity_id: "{{ entity_id }}" + operation: "clear" + + - alias: "Docker Containers Maintenance Prompt" + id: docker_containers_maintenance_prompt + description: "Prompt Carlo to snooze container alerts for maintenance when more than 3 containers are down." + mode: single + trigger: + - platform: numeric_state + entity_id: sensor.docker_containers_down_count + above: 3 + condition: + - condition: state + entity_id: binary_sensor.docker_container_alerts_snoozed + state: "off" action: - - service: repairs.create + - service: script.notify_engine_two_button data: - issue_id: "{{ issue_id }}" - title: "Container offline: {{ container_name }}" - description: >- - {{ container_name }} has been {{ current_state }} for over 5 minutes. - Entity: {{ trigger.entity_id }}. - severity: warning - persistent: true + title: "Docker Maintenance Check" + value1: "{{ states('sensor.docker_containers_down_count') }} containers are currently down." + value2: "Down: {{ states('sensor.docker_containers_down_list') }}" + who: "carlo" + group: "maintenance" + title1: "Yes, snooze 1h" + action1: "DOCKER_MAINTENANCE_SNOOZE_1H" + icon1: "sfsymbols:clock" + title2: "No, investigate" + action2: "DOCKER_MAINTENANCE_NOT_MAINTENANCE" + icon2: "sfsymbols:wrench.and.screwdriver" + - service: script.send_to_logbook + data: + topic: "DOCKER" + message: >- + Maintenance prompt sent to Carlo ({{ states('sensor.docker_containers_down_count') }} down: + {{ states('sensor.docker_containers_down_list') }}). - - alias: "Docker Container Offline - Clear Repairs" - id: docker_container_offline_repairs_clear - description: "Clear Repairs issue once the watched container is back online." - mode: parallel + - alias: "Docker Maintenance Snooze 1H" + id: docker_maintenance_snooze_1h + description: "Snooze dynamic container alerts for one hour from a notification action." + mode: single trigger: - - platform: state - entity_id: - - sensor.rc_price_checker_state - - switch.rc_price_checker_container + - platform: event + event_type: mobile_app_notification_action + event_data: + action: DOCKER_MAINTENANCE_SNOOZE_1H variables: - issue_id: "docker_container_{{ trigger.entity_id | replace('.', '_') }}" - condition: - - condition: template - value_template: >- - {{ states(trigger.entity_id) not in ['unavailable', 'unknown', 'restarting', 'off'] }} + snooze_until: "{{ (now() + timedelta(hours=1)).strftime('%Y-%m-%d %H:%M:%S') }}" + action: + - service: input_datetime.set_datetime + target: + entity_id: input_datetime.docker_container_alerts_snooze_until + data: + datetime: "{{ snooze_until }}" + - service: script.send_to_logbook + data: + topic: "DOCKER" + message: "Container alerts snoozed for 1 hour (until {{ snooze_until }})." + + - alias: "Docker Maintenance Declined" + id: docker_maintenance_declined + description: "Log when maintenance snooze is declined from the dynamic container prompt." + mode: single + trigger: + - platform: event + event_type: mobile_app_notification_action + event_data: + action: DOCKER_MAINTENANCE_NOT_MAINTENANCE action: - - service: repairs.remove - continue_on_error: true + - service: script.send_to_logbook data: - issue_id: "{{ issue_id }}" + topic: "DOCKER" + message: >- + Maintenance snooze declined with {{ states('sensor.docker_containers_down_count') }} + containers down ({{ states('sensor.docker_containers_down_list') }}).