From 89d12ee1e76c8258bc90a8d848721792e3a7d550 Mon Sep 17 00:00:00 2001 From: Carlo Costanzo Date: Wed, 12 Aug 2026 13:52:09 -0400 Subject: [PATCH] feat: surface BearClaw health and diagnostics --- .../partials/ai_diagnostics_sections.yaml | 38 ++ .../partials/joanna_activity_sections.yaml | 354 ------------------ .../partials/joanna_sections.yaml | 41 +- .../templates/button_card_templates.yaml | 42 +++ .../infrastructure/views/06_joanna.yaml | 2 +- ..._activity.yaml => 06b_ai_diagnostics.yaml} | 16 +- .../overview/partials/admin_sections.yaml | 71 ---- .../overview/partials/health_sections.yaml | 117 +++++- .../dashboards/overview/views/06_health.yaml | 3 +- .../dashboards/overview/views/08_admin.yaml | 18 - config/packages/bearclaw.yaml | 138 ++++++- tools/dashboard_entity_allowlist.txt | 12 +- 12 files changed, 348 insertions(+), 504 deletions(-) create mode 100644 config/dashboards/infrastructure/partials/ai_diagnostics_sections.yaml delete mode 100644 config/dashboards/infrastructure/partials/joanna_activity_sections.yaml rename config/dashboards/infrastructure/views/{06a_joanna_activity.yaml => 06b_ai_diagnostics.yaml} (59%) delete mode 100644 config/dashboards/overview/partials/admin_sections.yaml delete mode 100644 config/dashboards/overview/views/08_admin.yaml diff --git a/config/dashboards/infrastructure/partials/ai_diagnostics_sections.yaml b/config/dashboards/infrastructure/partials/ai_diagnostics_sections.yaml new file mode 100644 index 00000000..ab0ead56 --- /dev/null +++ b/config/dashboards/infrastructure/partials/ai_diagnostics_sections.yaml @@ -0,0 +1,38 @@ +###################################################################### +# @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 - AI Diagnostics +# Shows the current instruction and response pair for request tracing. +# ------------------------------------------------------------------- +# Notes: Included by config/dashboards/infrastructure/views/06b_ai_diagnostics.yaml. +###################################################################### + +- type: grid + column_span: 4 + columns: 1 + 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: AI Diagnostics + + - type: markdown + title: Response Received from OpenAI + content: >- + {{ state_attr('sensor.openai_response', 'response') or 'No response available.' }} + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml + + # Core markdown is required because long templated diagnostic payloads cannot be rendered safely by the shared button-card templates. + - type: markdown + title: Instructions Sent to OpenAI + content: >- + {{ state_attr('sensor.openai_instructions', 'instructions') or 'No instructions available.' }} + card_mod: + style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml diff --git a/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml b/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml deleted file mode 100644 index 3a16406e..00000000 --- a/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml +++ /dev/null @@ -1,354 +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 Partial - Joanna Activity -# Latest 12 dispatches from BearClaw's rolling 24-hour runtime history. -# ------------------------------------------------------------------- -# Notes: Rows intentionally read the dispatchActivity status attribute so the count and detail share one source of truth. -###################################################################### - -# ------------------------------------------------------------------- -# Dispatch summary -# ------------------------------------------------------------------- -- 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(4, minmax(0, 1fr)) - grid-auto-flow: row - grid-auto-rows: min-content - grid-gap: 12px - margin: 0 - mediaquery: - "(max-width: 900px)": - grid-template-columns: repeat(2, minmax(0, 1fr)) - cards: - - type: custom:button-card - template: bearstone_infra_kpi - entity: sensor.joanna_all_dispatches_24h - name: All Jobs · 24H - icon: mdi:robot - - - type: custom:button-card - template: bearstone_infra_kpi - entity: sensor.joanna_ha_dispatches_24h - name: HA Jobs · 24H - icon: mdi:home-automation - - - type: custom:button-card - template: bearstone_infra_kpi - entity: sensor.joanna_queue_pending - name: Queue - icon: mdi:playlist-clock - - - type: custom:button-card - template: bearstone_infra_status_kpi - entity: sensor.joanna_ha_errors_24h - name: HA Errors · 24H - icon: mdi:alert-circle-outline - variables: - ok_state: '0' - healthy_text: NONE - raw_suffix: ERRORS - -# ------------------------------------------------------------------- -# Recent dispatches -# ------------------------------------------------------------------- -- 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(2, minmax(0, 1fr)) - grid-auto-flow: row - grid-auto-rows: min-content - grid-gap: 12px - margin: 0 - mediaquery: - "(max-width: 900px)": - grid-template-columns: repeat(1, minmax(0, 1fr)) - 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: Most Recent Dispatches - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 0 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No dispatches in 24 hours'; - return String(item.source || 'unknown') - .replace(/^home_assistant_automation\./, 'HA · ') - .replace(/^scheduler_/, 'Scheduled · ') - .replace(/^manual_/, 'Manual · ') - .replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 1 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 2 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 3 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 4 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 5 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - 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: Earlier Today - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 6 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 7 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 8 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 9 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 10 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] - - - type: custom:button-card - template: bearstone_infra_list_row - entity: sensor.bearclaw_status_telemetry - icon: mdi:robot-outline - variables: - activity_index: 11 - name: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'No additional dispatches'; - return String(item.source || 'unknown').replace(/^home_assistant_automation\./, 'HA · ').replace(/^scheduler_/, 'Scheduled · ').replace(/^manual_/, 'Manual · ').replaceAll('_', ' '); - ]]] - state_display: > - [[[ - const item = entity?.attributes?.dispatchActivity?.items?.[variables?.activity_index]; - if (!item) return 'NO ACTIVITY'; - const state = String(item.state || 'queued').replaceAll('_', ' ').toUpperCase(); - const when = item.dispatchedAt ? new Date(item.dispatchedAt).toLocaleString('en-US', { month: 'short', day: 'numeric', hour: 'numeric', minute: '2-digit' }) : 'N/A'; - return `${state} · ${when}`; - ]]] diff --git a/config/dashboards/infrastructure/partials/joanna_sections.yaml b/config/dashboards/infrastructure/partials/joanna_sections.yaml index 3165f1fb..b3ac6158 100644 --- a/config/dashboards/infrastructure/partials/joanna_sections.yaml +++ b/config/dashboards/infrastructure/partials/joanna_sections.yaml @@ -6,7 +6,7 @@ # Infrastructure Partial - Joanna # Focused dispatch health, latest activity, rolling volume, and diagnostics. # ------------------------------------------------------------------- -# Notes: The activity drill-down reads the same rolling 24-hour runtime job history as the dispatch counters. +# Notes: BearClaw Admin provides the detailed runtime drill-down while this page stays summary-focused. # Notes: This view changes HA presentation only; BearClaw runtime behavior is untouched. # Notes: Reset expiry is amber within 7 days and red within 3 days; the four reset slots remain visible. ###################################################################### @@ -111,13 +111,13 @@ - type: custom:button-card template: bearstone_infra_list_row - entity: sensor.joanna_all_dispatches_24h - name: Open Joanna Activity - icon: mdi:timeline-text-outline - state_display: '[[[ return `${entity.state} dispatches in 24h`; ]]]' + entity: sensor.bearclaw_status_telemetry + name: BearClaw Admin + icon: mdi:shield-crown-outline + state_display: OPEN ADMIN tap_action: - action: navigate - navigation_path: /dashboard-infrastructure/joanna-activity + action: url + url_path: http://192.168.10.13:8124/admin - type: custom:vertical-stack-in-card card_mod: @@ -166,14 +166,14 @@ icon: mdi:calendar-month - type: custom:button-card - template: bearstone_infra_status_kpi - entity: binary_sensor.joanna_qmd_healthy - name: QMD Health - icon: mdi:heart-pulse - variables: - ok_state: 'on' - healthy_text: HEALTHY - alert_text: DEGRADED + template: bearstone_infra_list_row + entity: sensor.openai_response + name: AI Diagnostics + icon: mdi:message-text-outline + state_display: OPEN RESPONSE + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/ai-diagnostics - type: custom:button-card template: bearstone_infra_list_row @@ -245,7 +245,6 @@ const days = Number(credit.days_remaining); return `${when} | ${Number.isFinite(days) ? days.toFixed(1) + ' DAYS' : String(credit.status || '').toUpperCase()}`; ]]] - - type: custom:button-card template: bearstone_infra_list_row entity: sensor.codex_reset_credit_status @@ -287,13 +286,3 @@ const days = Number(credit.days_remaining); return `${when} | ${Number.isFinite(days) ? days.toFixed(1) + ' DAYS' : String(credit.status || '').toUpperCase()}`; ]]] - - - type: custom:button-card - template: bearstone_infra_status_kpi - entity: binary_sensor.joanna_session_source_indexed - name: Transcript Indexing - icon: mdi:message-text-clock-outline - variables: - ok_state: 'on' - healthy_text: ENABLED - alert_text: DISABLED diff --git a/config/dashboards/infrastructure/templates/button_card_templates.yaml b/config/dashboards/infrastructure/templates/button_card_templates.yaml index 28324456..667a1379 100644 --- a/config/dashboards/infrastructure/templates/button_card_templates.yaml +++ b/config/dashboards/infrastructure/templates/button_card_templates.yaml @@ -1222,15 +1222,57 @@ bearstone_overview_sleep_summary: bearstone_health_kpi: template: bearstone_infra_kpi + variables: + detail_layout: false styles: + grid: + - grid-template-areas: > + [[[ + return variables?.detail_layout + ? '"i" "n" "s" "l"' + : '"i" "n" "s"'; + ]]] + - grid-template-columns: minmax(0, 1fr) + - grid-template-rows: > + [[[ + return variables?.detail_layout + ? '22px min-content min-content minmax(18px, auto)' + : '22px min-content min-content'; + ]]] name: - font-size: 12px - font-weight: 900 - opacity: 0.8 + - justify-self: stretch + - width: 100% + - min-width: 0 + - white-space: normal + - overflow: visible + - text-overflow: clip + - line-height: 15px state: - font-size: 17px - font-weight: 900 - opacity: 1 + - justify-self: stretch + - width: 100% + - min-width: 0 + - white-space: normal + - overflow: visible + - text-overflow: clip + - line-height: 22px + label: + - font-size: 12px + - font-weight: 600 + - opacity: 0.78 + - justify-self: stretch + - width: 100% + - min-width: 0 + - white-space: normal + - overflow: visible + - text-overflow: clip + - text-align: center + - line-height: 16px bearstone_infra_update_row: template: bearstone_infra_list_row diff --git a/config/dashboards/infrastructure/views/06_joanna.yaml b/config/dashboards/infrastructure/views/06_joanna.yaml index 87732c54..1ed51be8 100644 --- a/config/dashboards/infrastructure/views/06_joanna.yaml +++ b/config/dashboards/infrastructure/views/06_joanna.yaml @@ -6,7 +6,7 @@ # Infrastructure View - Joanna # Focused dispatch health, latest activity, diagnostics, and reset status. # ------------------------------------------------------------------- -# Notes: Replaces the unbounded embedded logbook with a direct Activity link. +# Notes: BearClaw Admin owns detailed runtime history; this view stays summary-focused. ###################################################################### title: Joanna diff --git a/config/dashboards/infrastructure/views/06a_joanna_activity.yaml b/config/dashboards/infrastructure/views/06b_ai_diagnostics.yaml similarity index 59% rename from config/dashboards/infrastructure/views/06a_joanna_activity.yaml rename to config/dashboards/infrastructure/views/06b_ai_diagnostics.yaml index 6d2a507f..948c3ae9 100644 --- a/config/dashboards/infrastructure/views/06a_joanna_activity.yaml +++ b/config/dashboards/infrastructure/views/06b_ai_diagnostics.yaml @@ -3,20 +3,20 @@ # For more info visit https://www.vcloudinfo.com/click-here # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- -# Infrastructure Subview - Joanna Activity -# Latest runtime dispatches from the same 24-hour source as the counters. +# Infrastructure View - AI Diagnostics +# Compact diagnostic surface for the most recent OpenAI response. # ------------------------------------------------------------------- -# Notes: Hidden drill-down reached from the Joanna Dispatch Trend card. +# Notes: The Joanna AI Diagnostics row is the canonical entry point. ###################################################################### -title: Joanna Activity -path: joanna-activity -type: sections +title: AI Diagnostics +path: ai-diagnostics subview: true -back_path: /dashboard-infrastructure/joanna +icon: mdi:message-text-outline theme: default +type: sections max_columns: 4 dense_section_placement: true badges: [] -sections: !include /config/dashboards/infrastructure/partials/joanna_activity_sections.yaml +sections: !include /config/dashboards/infrastructure/partials/ai_diagnostics_sections.yaml cards: [] diff --git a/config/dashboards/overview/partials/admin_sections.yaml b/config/dashboards/overview/partials/admin_sections.yaml deleted file mode 100644 index eec0ab92..00000000 --- a/config/dashboards/overview/partials/admin_sections.yaml +++ /dev/null @@ -1,71 +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 -# ------------------------------------------------------------------- -# Overview Partial - admin sections -# Reusable list extracted from a view for smaller diffs and safer edits. -# ------------------------------------------------------------------- -# Notes: Extracted from config/dashboards/overview/views/08_admin.yaml key `sections`. -###################################################################### - -- type: grid - cards: - - type: tile - entity: automation.llm_vision_garage_cans_upper_right - - type: tile - entity: input_text.llmvision_garbage_last_keyframe - - type: tile - entity: input_datetime.llmvision_garbage_last_run - - type: tile - entity: input_text.llmvision_garbage_last_response - - type: tile - entity: calendar.llm_vision_timeline - - type: tile - entity: update.llm_vision_update - - type: tile - entity: input_button.llmvision_garbage_check - - type: tile - entity: binary_sensor.front_door_packages_present - - type: tile - entity: input_text.llmvision_front_door_last_response - - type: tile - entity: input_datetime.llmvision_front_door_last_run - - type: tile - entity: input_text.llmvision_front_door_last_keyframe - - type: tile - entity: input_button.llmvision_front_door_package_check -- type: grid - cards: - - type: markdown - title: AI Diagnostics - content: '**OpenAI Response:** - - - {{ state_attr(''sensor.openai_response'', ''response'') }} - - ' - - type: markdown - title: AI Diagnostics - content: '**OpenAI Instructions:** - - - {{ state_attr(''sensor.openai_instructions'', ''instructions'') }} - - ' -- type: grid - cards: - - camera_view: auto - type: picture-glance - title: Bedroom Tablet - image: https://demo.home-assistant.io/stub_config/kitchen.png - entities: - - entity: switch.alarm_panel_1_screensaver - image_entity: image.alarm_panel_1_screenshot - - camera_view: auto - type: picture-glance - title: Bedroom Tablet - image: https://demo.home-assistant.io/stub_config/kitchen.png - entities: - - entity: sensor.alarm_panel_1_battery - camera_image: camera.alarm_panel_1 diff --git a/config/dashboards/overview/partials/health_sections.yaml b/config/dashboards/overview/partials/health_sections.yaml index 345c35ae..664c9291 100644 --- a/config/dashboards/overview/partials/health_sections.yaml +++ b/config/dashboards/overview/partials/health_sections.yaml @@ -4,11 +4,12 @@ # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Overview Partial - Health -# Current SleepIQ state, paired nightly trends, and native bed controls. +# Current SleepIQ state, Joanna achievements and steps, paired nightly trends, and native bed controls. # ------------------------------------------------------------------- # Notes: Daily maximum statistics suppress SleepIQ's between-session zero states. # Notes: Direct integration entities are reused; no dashboard helper state is added. # Notes: The ribbon prioritizes occupancy, firmness, and current session duration. +# Notes: Step cards use each person's authoritative daily source with completed-day 7d averages and prior 28d/90d baselines. ###################################################################### # ------------------------------------------------------------------- @@ -103,6 +104,120 @@ return `${Math.floor(value)}h ${Math.round((value % 1) * 60)}m`; ]]] +# ------------------------------------------------------------------- +# HealthKit steps and personal records +# ------------------------------------------------------------------- +- 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(4, minmax(0, 1fr)) + grid-auto-flow: row + grid-auto-rows: min-content + grid-gap: 12px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_health_kpi + entity: sensor.joanna_carlo_steps_trend + name: Carlo Steps - 7d Avg + icon: mdi:walk + show_label: true + variables: + detail_layout: true + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${Math.round(value).toLocaleString()} / day` : 'NO DATA'; + ]]] + label: > + [[[ + const attrs = entity?.attributes || {}; + const value = (averageKey, samplesKey, windowDays) => { + const raw = attrs[averageKey]; + const number = raw === null || raw === undefined || raw === '' ? NaN : Number(raw); + if (!Number.isFinite(number)) return '—'; + const samples = Number(attrs[samplesKey] || 0); + const coverage = samples > 0 && samples < windowDays ? ` (${samples}d)` : ''; + return `${Math.round(number).toLocaleString()}${coverage}`; + }; + return `28d ${value('prior_28d_average', 'prior_28d_samples', 28)} · 90d ${value('prior_90d_average', 'prior_90d_samples', 90)}`; + ]]] + + - type: custom:button-card + template: bearstone_health_kpi + entity: sensor.joanna_stacey_steps_trend + name: Stacey Steps - 7d Avg + icon: mdi:walk + show_label: true + variables: + detail_layout: true + state_display: > + [[[ + const value = Number(entity?.state); + return Number.isFinite(value) ? `${Math.round(value).toLocaleString()} / day` : 'NO DATA'; + ]]] + label: > + [[[ + const attrs = entity?.attributes || {}; + const value = (averageKey, samplesKey, windowDays) => { + const raw = attrs[averageKey]; + const number = raw === null || raw === undefined || raw === '' ? NaN : Number(raw); + if (!Number.isFinite(number)) return '—'; + const samples = Number(attrs[samplesKey] || 0); + const coverage = samples > 0 && samples < windowDays ? ` (${samples}d)` : ''; + return `${Math.round(number).toLocaleString()}${coverage}`; + }; + return `28d ${value('prior_28d_average', 'prior_28d_samples', 28)} · 90d ${value('prior_90d_average', 'prior_90d_samples', 90)}`; + ]]] + + - type: custom:button-card + template: bearstone_health_kpi + entity: sensor.joanna_carlo_latest_personal_record + name: Carlo Personal Record + icon: mdi:trophy-outline + show_label: true + variables: + detail_layout: true + state_display: > + [[[ + return entity?.attributes?.display_value || 'NO RECORDS'; + ]]] + label: > + [[[ + const attrs = entity?.attributes || {}; + const pieces = [entity?.state, attrs.activity_type, attrs.achieved_on].filter((value) => value && value !== 'unknown'); + return pieces.join(' · ') || 'Stored HealthKit history'; + ]]] + + - type: custom:button-card + template: bearstone_health_kpi + entity: sensor.joanna_stacey_latest_personal_record + name: Stacey Personal Record + icon: mdi:trophy-outline + show_label: true + variables: + detail_layout: true + state_display: > + [[[ + return entity?.attributes?.display_value || 'NO RECORDS'; + ]]] + label: > + [[[ + const attrs = entity?.attributes || {}; + const pieces = [entity?.state, attrs.activity_type, attrs.achieved_on].filter((value) => value && value !== 'unknown'); + return pieces.join(' · ') || 'Stored HealthKit history'; + ]]] + # ------------------------------------------------------------------- # Fourteen-night paired trends # ------------------------------------------------------------------- diff --git a/config/dashboards/overview/views/06_health.yaml b/config/dashboards/overview/views/06_health.yaml index 920834b2..1ac73528 100644 --- a/config/dashboards/overview/views/06_health.yaml +++ b/config/dashboards/overview/views/06_health.yaml @@ -4,10 +4,11 @@ # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Overview View - health -# SleepIQ health metrics view for Carlo and Stacey. +# SleepIQ and Joanna health insights for Carlo and Stacey. # ------------------------------------------------------------------- # Related Issue: 1584 # Notes: Promoted from the approved Health pilot; personal health remains with Overview. +# Notes: Joanna supplies shared personal records and completed-day 7d vs 28d vs 90d step trends. ###################################################################### title: Health diff --git a/config/dashboards/overview/views/08_admin.yaml b/config/dashboards/overview/views/08_admin.yaml deleted file mode 100644 index 13ca929f..00000000 --- a/config/dashboards/overview/views/08_admin.yaml +++ /dev/null @@ -1,18 +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 -# ------------------------------------------------------------------- -# Overview View - Admin -# YAML-exported Lovelace dashboard (split into view files). -# ------------------------------------------------------------------- -# Notes: Exported from config/.storage/lovelace.lovelace view index 8. -###################################################################### - -title: Admin -icon: mdi:security -visible: -- user: be280a93c9d7416e98d25d0470f414be -cards: [] -type: sections -sections: !include /config/dashboards/overview/partials/admin_sections.yaml diff --git a/config/packages/bearclaw.yaml b/config/packages/bearclaw.yaml index 48f460be..d9177a59 100644 --- a/config/packages/bearclaw.yaml +++ b/config/packages/bearclaw.yaml @@ -16,6 +16,8 @@ # Notes: Hourly TeslaMate road-trip milestone/final callbacks become compact family mobile summaries; Joanna sends full reports to Carlo and Stacey. # Notes: Status telemetry polling expects !secret bearclaw_status_url (token header stays !secret bearclaw_token). # Notes: Status telemetry includes the latest 12 dispatches in the rolling 24-hour window for the Joanna Activity drill-down. +# Notes: Status telemetry also exposes compact, user-keyed step trends and personal records for the Health dashboard. +# Notes: Retired QMD health telemetry is no longer imported or materialized in Home Assistant. # Notes: Codex reset-credit expiry telemetry is sourced from the appliance `codexUsage` status field. # Notes: Reset-credit urgency is dashboard-only: warning within 7 days and critical within 3 days; no Repair is created. # Notes: Codex reset walkthrough: https://youtu.be/7wKhtrvtyiI @@ -147,8 +149,8 @@ sensor: - transports - followups - codexUsage - - qmdHealth - memoryIndex + - healthInsights template: - sensor: @@ -332,15 +334,6 @@ template: 0 {% endif %} - - name: Joanna QMD Consecutive Failures - unique_id: joanna_qmd_consecutive_failures - icon: mdi:heart-pulse - unit_of_measurement: checks - state_class: measurement - state: >- - {% set qmd = state_attr('sensor.bearclaw_status_telemetry', 'qmdHealth') | default({}, true) %} - {{ qmd.get('consecutiveFailures', 0) | int(0) }} - - name: Joanna Memory Docs unique_id: joanna_memory_docs icon: mdi:file-document-multiple-outline @@ -374,14 +367,127 @@ template: 0 {% endif %} - - binary_sensor: - - name: Joanna QMD Healthy - unique_id: joanna_qmd_healthy - icon: mdi:heart-pulse + - name: Joanna Carlo Steps Trend + unique_id: joanna_carlo_steps_trend + icon: mdi:walk + unit_of_measurement: steps/day + state_class: measurement + availability: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DayAverage') not in [none, 'unknown', 'unavailable', ''] }} state: >- - {% set qmd = state_attr('sensor.bearclaw_status_telemetry', 'qmdHealth') | default({}, true) %} - {{ qmd.get('healthy', false) in [true, 'true', 'True', 'on', 'yes', 1, '1'] }} + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DayAverage', 0) | int(0) }} + attributes: + prior_28d_average: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior28DayAverage') }} + prior_90d_average: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior90DayAverage') }} + change_vs_28_pct: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('changeVs28Percent') }} + change_vs_90_pct: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('changeVs90Percent') }} + current_7d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('current7DaySamples', 0) | int(0) }} + prior_28d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior28DaySamples', 0) | int(0) }} + prior_90d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('prior90DaySamples', 0) | int(0) }} + completed_through: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('steps', {}).get('completedThroughDayKey') }} + - name: Joanna Stacey Steps Trend + unique_id: joanna_stacey_steps_trend + icon: mdi:walk + unit_of_measurement: steps/day + state_class: measurement + availability: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DayAverage') not in [none, 'unknown', 'unavailable', ''] }} + state: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DayAverage', 0) | int(0) }} + attributes: + prior_28d_average: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior28DayAverage') }} + prior_90d_average: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior90DayAverage') }} + change_vs_28_pct: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('changeVs28Percent') }} + change_vs_90_pct: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('changeVs90Percent') }} + current_7d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('current7DaySamples', 0) | int(0) }} + prior_28d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior28DaySamples', 0) | int(0) }} + prior_90d_samples: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('prior90DaySamples', 0) | int(0) }} + completed_through: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('steps', {}).get('completedThroughDayKey') }} + + - name: Joanna Carlo Latest Personal Record + unique_id: joanna_carlo_latest_personal_record + icon: mdi:trophy-outline + availability: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest') is mapping }} + state: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('label', 'No records') }} + attributes: + display_value: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('displayValue') }} + activity_type: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('activityType') }} + achieved_on: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('latest', {}).get('dayKey') }} + record_count: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('carlo', {}).get('personalRecords', {}).get('count', 0) | int(0) }} + + - name: Joanna Stacey Latest Personal Record + unique_id: joanna_stacey_latest_personal_record + icon: mdi:trophy-outline + availability: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest') is mapping }} + state: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('label', 'No records') }} + attributes: + display_value: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('displayValue') }} + activity_type: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('activityType') }} + achieved_on: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('latest', {}).get('dayKey') }} + record_count: >- + {% set health = state_attr('sensor.bearclaw_status_telemetry', 'healthInsights') | default({}, true) %} + {{ health.get('users', {}).get('stacey', {}).get('personalRecords', {}).get('count', 0) | int(0) }} + + - binary_sensor: - name: Joanna Session Source Indexed unique_id: joanna_session_source_indexed icon: mdi:message-text-clock-outline diff --git a/tools/dashboard_entity_allowlist.txt b/tools/dashboard_entity_allowlist.txt index 6cad2382..00ce2c21 100644 --- a/tools/dashboard_entity_allowlist.txt +++ b/tools/dashboard_entity_allowlist.txt @@ -1,10 +1,7 @@ # Static dashboard entities supplied by UI-managed helpers or integrations. # Keep sorted. The validator proves these exist in live HA storage when available. -automation.llm_vision_garage_cans_upper_right binary_sensor.codex_reset_credit_expiring_soon binary_sensor.garage_phyn_leak_alert -binary_sensor.joanna_qmd_healthy -binary_sensor.joanna_session_source_indexed binary_sensor.mcu1_gpio10 binary_sensor.mcu1_gpio13 binary_sensor.mcu1_gpio14 @@ -28,8 +25,6 @@ button.l10s_vacuum_reset_filter button.l10s_vacuum_reset_main_brush button.l10s_vacuum_reset_mop_pad button.l10s_vacuum_reset_side_brush -calendar.llm_vision_timeline -camera.alarm_panel_1 camera.bedroomgate camera.filtergate camera.frontlawn @@ -51,7 +46,6 @@ select.l10s_vacuum_auto_empty_frequency select.l10s_vacuum_mop_pad_humidity select.l10s_vacuum_suction_level select.sleepnumber_carlo_foundation_preset_left -sensor.alarm_panel_1_battery sensor.carlo_location_display sensor.carlo_nas01_temperature sensor.carlo_nas01_volume_1_used_space @@ -68,13 +62,16 @@ sensor.garage_ups_battery_runtime sensor.joanna_all_dispatches_24h sensor.joanna_all_dispatches_30d sensor.joanna_all_dispatches_7d +sensor.joanna_carlo_latest_personal_record +sensor.joanna_carlo_steps_trend sensor.joanna_ha_dispatches_24h sensor.joanna_ha_dispatches_30d sensor.joanna_ha_dispatches_7d sensor.joanna_ha_errors_24h sensor.joanna_ha_success_rate_24h sensor.joanna_memory_docs -sensor.joanna_queue_pending +sensor.joanna_stacey_latest_personal_record +sensor.joanna_stacey_steps_trend sensor.justin_location_display sensor.l10s_vacuum_battery_level sensor.l10s_vacuum_cleaning_count @@ -140,5 +137,4 @@ sensor.water_softener_salt_total_added switch.l10s_vacuum_auto_dust_collecting switch.phyn_autoshutoff_enabled switch.phyn_scheduled_leak_test_enabled -update.llm_vision_update weather.pirateweather