From 7908ee7b495196fc94d0ce65f97a81cd80976d77 Mon Sep 17 00:00:00 2001 From: Carlo Costanzo Date: Thu, 12 Mar 2026 18:13:37 -0400 Subject: [PATCH] Enhance Home Assistant configuration and dashboards - Updated configuration.yaml to include trusted users for network authentication. - Added BearClaw status telemetry sensor and related KPI sensors in bearclaw.yaml for improved monitoring. - Enhanced infrastructure dashboard with a new Joanna Dispatch button card for telemetry display. - Updated README.md to reflect new BearClaw telemetry features and sensor integrations. --- config/configuration.yaml | 5 +- .../dashboards/infrastructure/dashboard.yaml | 1 + .../partials/home_sections.yaml | 23 ++- .../partials/joanna_sections.yaml | 172 ++++++++++++++++++ .../infrastructure/views/06_joanna.yaml | 20 ++ config/packages/README.md | 2 +- config/packages/bearclaw.yaml | 135 ++++++++++++++ config/travis_secrets.yaml | 1 + tools/ha_ui_smoke.ps1 | 1 + 9 files changed, 354 insertions(+), 6 deletions(-) create mode 100644 config/dashboards/infrastructure/partials/joanna_sections.yaml create mode 100644 config/dashboards/infrastructure/views/06_joanna.yaml diff --git a/config/configuration.yaml b/config/configuration.yaml index c6f216f1..e4adb844 100755 --- a/config/configuration.yaml +++ b/config/configuration.yaml @@ -15,12 +15,15 @@ homeassistant: initial_state: 'on' packages: !include_dir_named packages auth_providers: - - type: homeassistant - type: trusted_networks trusted_networks: - 192.168.10.0/24 # - !secret external_ip + trusted_users: + 192.168.10.17: be280a93c9d7416e98d25d0470f414be + 192.168.10.88: be280a93c9d7416e98d25d0470f414be allow_bypass_login: true + - type: homeassistant default_config: diff --git a/config/dashboards/infrastructure/dashboard.yaml b/config/dashboards/infrastructure/dashboard.yaml index 159b6606..1bf054d1 100644 --- a/config/dashboards/infrastructure/dashboard.yaml +++ b/config/dashboards/infrastructure/dashboard.yaml @@ -7,6 +7,7 @@ # YAML-exported Lovelace dashboard (split into view files). # ------------------------------------------------------------------- # Notes: Entry point for dashboard key `dashboard-infrastructure`. Views are loaded from /config/dashboards/infrastructure/views. +# Notes: Joanna dispatch telemetry view is available at `/dashboard-infrastructure/joanna`. ###################################################################### button_card_templates: !include /config/dashboards/infrastructure/templates/button_card_templates.yaml diff --git a/config/dashboards/infrastructure/partials/home_sections.yaml b/config/dashboards/infrastructure/partials/home_sections.yaml index 1f61ecf9..d9fcec1e 100644 --- a/config/dashboards/infrastructure/partials/home_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_sections.yaml @@ -40,7 +40,22 @@ cards: - type: custom:button-card template: bearstone_infra_panel_header - name: Alerts + name: Infrastructure Status + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.joanna_ha_dispatches_24h + name: Joanna Dispatch + icon: mdi:robot + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/joanna + state_display: > + [[[ + const ha24 = states['sensor.joanna_ha_dispatches_24h']?.state ?? '0'; + const all24 = states['sensor.joanna_all_dispatches_24h']?.state ?? '0'; + const queue = states['sensor.joanna_queue_pending']?.state ?? '0'; + return `HA ${ha24} | All ${all24} | Q ${queue}`; + ]]] - type: custom:button-card template: bearstone_infra_list_row @@ -503,7 +518,7 @@ name: UPS Battery icon: mdi:battery-70 state_display: > - [[[ + [[[ const v = Number(entity?.state); return Number.isFinite(v) ? `${v.toFixed(0)}%` : (entity?.state ?? 'unknown'); ]]] @@ -514,7 +529,7 @@ name: Runtime Remaining icon: mdi:timer-outline state_display: > - [[[ + [[[ const secs = parseInt(entity?.state, 10); if (!Number.isFinite(secs)) return entity?.state ?? 'unknown'; const hours = Math.floor(secs / 3600); @@ -528,7 +543,7 @@ name: On Battery icon: mdi:battery-alert-variant-outline state_display: > - [[[ + [[[ const s = String(entity?.state || '').toUpperCase(); return s.includes('OB') ? 'Yes' : 'No'; ]]] diff --git a/config/dashboards/infrastructure/partials/joanna_sections.yaml b/config/dashboards/infrastructure/partials/joanna_sections.yaml new file mode 100644 index 00000000..0a5f47da --- /dev/null +++ b/config/dashboards/infrastructure/partials/joanna_sections.yaml @@ -0,0 +1,172 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# For more info visit https://www.vcloudinfo.com/click-here +# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig +# ------------------------------------------------------------------- +# Infrastructure Partial - Joanna sections +# Joanna/BearClaw dispatch KPI, trend, latest-state, and filtered activity panels. +# ------------------------------------------------------------------- +# Notes: Reuses shared bearstone_infra_* templates and standard infra card_mod snippets. +###################################################################### + +- type: grid + column_span: 4 + columns: 6 + square: false + cards: + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_ha_dispatches_24h + name: HA 24H + icon: mdi:calendar-clock + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_ha_dispatches_7d + name: HA 7D + icon: mdi:calendar-range + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_ha_dispatches_30d + name: HA 30D + icon: mdi:calendar-month + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_ha_errors_24h + name: HA Errors 24H + icon: mdi:alert-circle-outline + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_all_dispatches_24h + name: All 24H + icon: mdi:calendar-clock + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_all_dispatches_7d + name: All 7D + icon: mdi:calendar-range + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.joanna_all_dispatches_30d + name: All 30D + icon: mdi:calendar-month + +- type: grid + column_span: 4 + columns: 2 + square: false + cards: + - type: custom:vertical-stack-in-card + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: HA Dispatch Trend + - type: custom:mini-graph-card + name: HA Dispatches (24h Window) + icon: mdi:chart-timeline-variant + hours_to_show: 168 + points_per_hour: 1 + line_width: 2 + smoothing: true + show: + legend: false + labels: false + name: true + icon: true + state: true + entities: + - entity: sensor.joanna_ha_dispatches_24h + name: HA 24h + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml + + - type: custom:vertical-stack-in-card + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Latest Snapshot + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.bearclaw_status_telemetry + name: Latest HA Dispatch + icon: mdi:robot + state_display: > + [[[ + const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {}; + const latest = stats?.latest?.haAutomation || {}; + const source = latest?.source || 'none'; + const when = latest?.dispatchedAt || 'n/a'; + return `${source} | ${when}`; + ]]] + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.bearclaw_status_telemetry + name: Latest HA Completion + icon: mdi:check-circle-outline + state_display: > + [[[ + const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {}; + const latest = stats?.latest?.haAutomation || {}; + const state = latest?.state || 'none'; + const when = latest?.completedAt || 'n/a'; + return `${state} | ${when}`; + ]]] + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.bearclaw_status_telemetry + name: Latest Any Dispatch + icon: mdi:robot-excited + state_display: > + [[[ + const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {}; + const latest = stats?.latest?.all || {}; + const source = latest?.source || 'none'; + const when = latest?.dispatchedAt || 'n/a'; + return `${source} | ${when}`; + ]]] + - type: custom:button-card + template: bearstone_infra_list_row + entity: sensor.bearclaw_status_telemetry + name: HA Success (24h) + icon: mdi:percent-circle + state_display: > + [[[ + const v = states['sensor.joanna_ha_success_rate_24h']?.state ?? '0'; + return `${v}%`; + ]]] + +- type: grid + column_span: 4 + columns: 1 + square: false + cards: + - type: custom:layout-card + grid_options: + columns: full + layout_type: custom:grid-layout + layout: + grid-template-columns: repeat(1, minmax(0, 1fr)) + grid-auto-flow: row + grid-auto-rows: min-content + grid-gap: 12px + margin: 0 + cards: + - type: custom:vertical-stack-in-card + grid_options: + columns: full + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Joanna Activity + # Fallback card: Lovelace logbook card is required to show filtered activity history. + - type: logbook + hours_to_show: 72 + entities: + - automation.bearclaw_reply_webhook + - automation.tugtainer_dispatch_joanna_for_available_updates + - script.joanna_dispatch diff --git a/config/dashboards/infrastructure/views/06_joanna.yaml b/config/dashboards/infrastructure/views/06_joanna.yaml new file mode 100644 index 00000000..dbfabf37 --- /dev/null +++ b/config/dashboards/infrastructure/views/06_joanna.yaml @@ -0,0 +1,20 @@ +###################################################################### +# @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 - Joanna +# Dispatch and activity telemetry for BearClaw/Joanna. +# ------------------------------------------------------------------- +# Notes: Uses telemetry from /api/bearclaw/status ingested by bearclaw.yaml. +###################################################################### + +type: sections +title: Joanna +path: joanna +max_columns: 4 +icon: mdi:robot +theme: default +badges: [] +sections: !include /config/dashboards/infrastructure/partials/joanna_sections.yaml +cards: [] diff --git a/config/packages/README.md b/config/packages/README.md index f20e8fd0..0bac7eb3 100755 --- a/config/packages/README.md +++ b/config/packages/README.md @@ -52,7 +52,7 @@ Live collection of plug-and-play Home Assistant packages. Each YAML file in this | [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` | | [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, relay replies back, and write JOANNA webhook reply summaries to Activity feed. | `rest_command.bearclaw_*`, `automation.bearclaw_*`, `script.send_to_logbook`, webhook relay | +| [bearclaw.yaml](bearclaw.yaml) | Joanna/BearClaw bridge automations that forward Telegram commands to codex_appliance, relay replies back, ingest `/api/bearclaw/status` telemetry, and expose dispatch KPI sensors for Infrastructure dashboards. | `rest_command.bearclaw_*`, `sensor.bearclaw_status_telemetry`, `sensor.joanna_*`, `automation.bearclaw_*`, `script.send_to_logbook` | | [telegram_bot.yaml](telegram_bot.yaml) | Telegram script wrappers used by BearClaw and other ops flows (UI integration remains the source for bot config). | `script.joanna_send_telegram`, `telegram_bot.send_message` | | [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` | | [water_delivery.yaml](water_delivery.yaml) | ReadyRefresh delivery date helper with night-before + garage door Alexa reminders, plus helper-change audit logging and Telegram confirmations. | `input_datetime.water_delivery_date`, `script.send_to_logbook`, `script.joanna_send_telegram`, `notify.alexa_media_garage` | diff --git a/config/packages/bearclaw.yaml b/config/packages/bearclaw.yaml index 82426c8e..0875d445 100644 --- a/config/packages/bearclaw.yaml +++ b/config/packages/bearclaw.yaml @@ -13,6 +13,8 @@ # Notes: Telegram inline button callbacks are handled here and mapped to BearClaw commands. # Notes: Inbound Telegram handling enforces user_id + chat_id allowlists from secrets CSV values. # Notes: Reply webhook writes JOANNA activity entries to logbook for traceability. +# Notes: Status telemetry polling expects !secret bearclaw_status_url (token header stays !secret bearclaw_token). +# Notes: Blog: https://www.vcloudinfo.com/2026/03/joanna-dispatch-telemetry-home-assistant-infrastructure-dashboard/ ###################################################################### rest_command: @@ -43,6 +45,139 @@ rest_command: "wake": {{ wake | default(false) | tojson }}, "source": "home_assistant" } +sensor: + - platform: rest + name: BearClaw Status Telemetry + unique_id: bearclaw_status_telemetry + resource: !secret bearclaw_status_url + method: GET + scan_interval: 60 + timeout: 30 + headers: + x-codex-token: !secret bearclaw_token + value_template: "{{ 'ok' if value_json.ok | default(false) else 'error' }}" + json_attributes: + - dispatchStats + - queue + - active + +template: + - sensor: + - name: Joanna HA Dispatches 24h + unique_id: joanna_ha_dispatches_24h + icon: mdi:calendar-clock + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna HA Dispatches 7d + unique_id: joanna_ha_dispatches_7d + icon: mdi:calendar-range + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('d7', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna HA Dispatches 30d + unique_id: joanna_ha_dispatches_30d + icon: mdi:calendar-month + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('d30', {}).get('haAutomation', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna HA Completed 24h + unique_id: joanna_ha_completed_24h + icon: mdi:check-decagram + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('completed', 0) | int(0) }} + + - name: Joanna HA Success Rate 24h + unique_id: joanna_ha_success_rate_24h + icon: mdi:percent-circle + unit_of_measurement: '%' + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('successRatePct', 0) | float(0) }} + + - name: Joanna HA Errors 24h + unique_id: joanna_ha_errors_24h + icon: mdi:alert-circle-outline + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {% set outcomes = stats.get('windows', {}).get('h24', {}).get('haAutomation', {}).get('outcomes', {}) %} + {{ (outcomes.get('error', 0) | int(0)) + (outcomes.get('notify_error', 0) | int(0)) }} + + - name: Joanna All Dispatches 24h + unique_id: joanna_all_dispatches_24h + icon: mdi:calendar-clock + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('h24', {}).get('all', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna All Dispatches 7d + unique_id: joanna_all_dispatches_7d + icon: mdi:calendar-range + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('d7', {}).get('all', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna All Dispatches 30d + unique_id: joanna_all_dispatches_30d + icon: mdi:calendar-month + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {{ stats.get('windows', {}).get('d30', {}).get('all', {}).get('dispatched', 0) | int(0) }} + + - name: Joanna Queue Pending + unique_id: joanna_queue_pending + icon: mdi:playlist-clock + unit_of_measurement: jobs + state_class: measurement + state: >- + {% set queue = state_attr('sensor.bearclaw_status_telemetry', 'queue') | default({}, true) %} + {{ queue.get('pending', 0) | int(0) }} + + - name: Joanna Active Job Minutes + unique_id: joanna_active_job_minutes + icon: mdi:timer-outline + unit_of_measurement: min + state_class: measurement + state: >- + {% set active = state_attr('sensor.bearclaw_status_telemetry', 'active') | default({}, true) %} + {% set elapsed_ms = active.get('elapsedMs', 0) | int(0) %} + {{ (elapsed_ms / 60000) | round(1) }} + + - name: Joanna Minutes Since HA Dispatch + unique_id: joanna_minutes_since_ha_dispatch + icon: mdi:clock-fast + unit_of_measurement: min + state_class: measurement + state: >- + {% set stats = state_attr('sensor.bearclaw_status_telemetry', 'dispatchStats') | default({}, true) %} + {% set dispatched_at = stats.get('latest', {}).get('haAutomation', {}).get('dispatchedAt') %} + {% set dispatched_ts = as_timestamp(dispatched_at, 0) %} + {% if dispatched_ts > 0 %} + {{ ((as_timestamp(now()) - dispatched_ts) / 60) | round(1) }} + {% else %} + 0 + {% endif %} automation: - id: bearclaw_telegram_bear_command alias: BearClaw Telegram Bear Command diff --git a/config/travis_secrets.yaml b/config/travis_secrets.yaml index fb02cd2a..db4402ad 100755 --- a/config/travis_secrets.yaml +++ b/config/travis_secrets.yaml @@ -86,3 +86,4 @@ wolframalpha_chanukkah_api: https://api.wolframalpha.com/v2/result?appid=JIUY8U- wolframalpha_labor_api: https://api.wolframalpha.com/v2/result?appid=JIUY8U-4V8KY45VT1&i=How%20many%20days%20until%labor%20day wolframalpha_memorial_api: https://api.wolframalpha.com/v2/result?appid=JIUY8U-4V8KY45VT1&i=How%20many%20days%20until%20memorial wolframalpha_thanksgiving_api: https://api.wolframalpha.com/v2/result?appid=JIUY8U-4V8KY45VT1&i=How%20many%20days%20until%20thanksgiving +bearclaw_status_url: http://docker17.local:8124/api/bearclaw/status diff --git a/tools/ha_ui_smoke.ps1 b/tools/ha_ui_smoke.ps1 index ff41a808..45e623d0 100644 --- a/tools/ha_ui_smoke.ps1 +++ b/tools/ha_ui_smoke.ps1 @@ -40,6 +40,7 @@ $targets = @( '/profile', '/dashboard-infrastructure', '/dashboard-infrastructure/home', + '/dashboard-infrastructure/joanna', '/dashboard-infrastructure/docker', '/dashboard-kiosk' )