diff --git a/config/custom_templates/infrastructure_health.jinja b/config/custom_templates/infrastructure_health.jinja index 2101d68d..1f541612 100644 --- a/config/custom_templates/infrastructure_health.jinja +++ b/config/custom_templates/infrastructure_health.jinja @@ -36,6 +36,44 @@ }) %} {%- endmacro %} +{% macro scheduled_job_issues(returns) -%} + {# One row per reported job; retain a visible fallback for missing/truncated telemetry. #} + {% set entity_id = 'sensor.bearclaw_scheduled_job_health' %} + {% set status = states(entity_id) %} + {% set raw = state_attr(entity_id, 'issues') %} + {% set rows = raw | select('mapping') | list if raw is sequence and raw is not string and raw is not mapping else [] %} + {% set ns = namespace(issues=[]) %} + {% if status in ['warning', 'error'] %} + {% for row in rows %} + {% set ns.issues = ns.issues + [dict(row, count=1)] %} + {% endfor %} + {% set remaining = [state_attr(entity_id, 'issue_count') | int(0), rows | count, 1] | max - rows | count %} + {% if remaining > 0 %} + {% set ns.issues = ns.issues + [{ + 'label': (remaining ~ ' more scheduled job issues') if rows else 'Scheduled job details unavailable', + 'severity': status, 'count': remaining, + 'detail': 'Open BearClaw Admin > Activity > Scheduled Jobs to review the full report.' + }] %} + {% endif %} + {% elif status != 'ok' %} + {% set ns.issues = [{ + 'label': 'Scheduled job health unavailable', 'severity': 'error', 'count': 1, + 'detail': 'Check the appliance status connection. Last-known job details cannot confirm current health.' + }] %} + {% endif %} + {% do returns(ns.issues) %} +{%- endmacro %} + +{% macro dashboard_active_issue_count(returns) -%} + {% set active = (dashboard_active_entities | as_function)() %} + {% set ns = namespace(count=active | count) %} + {% if 'sensor.bearclaw_scheduled_job_health' in active %} + {% set jobs = (scheduled_job_issues | as_function)() %} + {% set ns.count = ns.count - 1 + (jobs | sum(attribute='count')) %} + {% endif %} + {% do returns([ns.count, states('sensor.active_issues') | int(0)] | max) %} +{%- endmacro %} + {% macro dashboard_severity(returns) -%} {# Match the Active Issue rows; an unrelated warning must never mask a fault. #} {% set active = (dashboard_active_entities | as_function)() %} @@ -127,13 +165,13 @@ 'sensor.mariadb_status': 'running', 'sensor.bearclaw_scheduled_job_health': 'ok', 'sensor.joanna_security_updates': 'review', - 'sensor.joanna_onenote_kb_health': 'ok', + 'sensor.joanna_onenote_kb_health': ['ok', 'indexing', 'recovering'], 'binary_sensor.joanna_mini_app_problem': 'off', 'binary_sensor.joanna_memory_index_stale': 'off' } %} {% set ns = namespace(active=[]) %} {% for entity_id, healthy in monitored.items() %} - {% if states(entity_id) != healthy %} + {% if states(entity_id) not in (healthy if healthy is not string else [healthy]) %} {% set ns.active = ns.active + [entity_id] %} {% endif %} {% endfor %} diff --git a/config/packages/bearclaw.yaml b/config/packages/bearclaw.yaml index e46a2e89..648badc7 100644 --- a/config/packages/bearclaw.yaml +++ b/config/packages/bearclaw.yaml @@ -20,12 +20,14 @@ # Notes: Status telemetry includes the latest 12 dispatches in the rolling 24-hour window for the Joanna Activity drill-down. # Notes: Status telemetry exposes compact scheduled-job warning/error health for the Systems dashboard. # Notes: Critical appliance updates open one operator-only Repair; vendor waits stay amber and never dispatch self-updates. +# Notes: The copyable Codex security prompt contains aggregate context and retrieves fresh private evidence when used. # Notes: Status telemetry distinguishes the compact BearClaw working-memory index from the federated OneNote KB. # Notes: Independent Mini App origin/public probes run every minute; sustained failures create Repairs and HA mobile alerts. # Notes: The Mini App incident latch survives restarts to avoid duplicate outage notifications; recovery clears its owned Repair. # Notes: the existing OneNote health entity reports account-owned indexer health. # Notes: Status telemetry exposes user-keyed step trends for Health and personal records for briefings and kiosks. # Notes: Status telemetry exposes 48-hour Carlo/Stacey bowling highlights for the Kitchen Show rotation. +# Notes: Family agenda is a filtered read-only list for kiosks; guest/vacation modes hide the TV page. # Notes: Retired QMD health telemetry is no longer imported or materialized in Home Assistant. # Notes: TeslaMate road-trip walkthrough: https://youtu.be/9-9T6v17NEw # Notes: TeslaMate road-trip companion post: https://www.vcloudinfo.com/2026/07/teslamate-road-trip-reports-codex.html @@ -184,6 +186,7 @@ sensor: - oneNoteIndexers - healthInsights - bowlingHighlights + - familyAgenda - scheduledJobsHealth - securityUpdates @@ -212,6 +215,8 @@ template: {{ (state_attr('sensor.bearclaw_status_telemetry', 'securityUpdates') | default({}, true)).get('counts', {}).get('needs_attention', 0) }} services_need_review: >- {{ (state_attr('sensor.bearclaw_status_telemetry', 'securityUpdates') | default({}, true)).get('needsReviewServices', 0) }} + codex_prompt: >- + {{ (state_attr('sensor.bearclaw_status_telemetry', 'securityUpdates') | default({}, true)).get('codexPrompt', '') }} automatic_fix_pending: >- {{ (state_attr('sensor.bearclaw_status_telemetry', 'securityUpdates') | default({}, true)).get('counts', {}).get('automatic_fix_pending', 0) }} waiting_vendor_patch: >- @@ -266,6 +271,12 @@ template: {% set indexes = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteIndexers') | default({}, true) %} {{ indexes.get('status', 'warning') }} attributes: + detail: >- + {% set indexes = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteIndexers') | default({}, true) %} + {{ indexes.get('detail', '') }} + pending_embeddings: >- + {% set indexes = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteIndexers') | default({}, true) %} + {{ indexes.get('pendingEmbeddings', 0) | int(0) }} docs: >- {% set indexes = state_attr('sensor.bearclaw_status_telemetry', 'oneNoteIndexers') | default({}, true) %} {{ indexes.get('docs', 0) | int(0) }} diff --git a/config/packages/infrastructure.yaml b/config/packages/infrastructure.yaml index c5fb4cff..ad55260d 100644 --- a/config/packages/infrastructure.yaml +++ b/config/packages/infrastructure.yaml @@ -15,6 +15,7 @@ # Related Issue: 1890 # Notes: Home dashboard consumes `infra_*` entities for exceptions-only alerts. # Notes: Systems uses the highest active severity: critical overrides warning; no issues is healthy. +# Notes: Scheduled-job issues count individually and share their detail rows across Systems and Joanna. # Notes: Cloudflare alerts use an authenticated Nabu Casa webhook, persistent Repairs, and owned-resource-only Joanna dispatch. # Notes: Certificate and unowned-domain alerts require human review; no Cloudflare DNS or security writes are authorized by an alert. # Notes: Cloudflare delivery is webhook-only; origin recovery reuses reports from existing website probes. @@ -276,9 +277,8 @@ template: unique_id: infra_dashboard_active_issue_count icon: mdi:alert-circle-check-outline state: >- - {% from 'infrastructure_health.jinja' import dashboard_active_entities %} - {% set active = dashboard_active_entities | as_function %} - {{ [active() | count, states('sensor.active_issues') | int(0)] | max }} + {% from 'infrastructure_health.jinja' import dashboard_active_issue_count %} + {{ (dashboard_active_issue_count | as_function)() }} attributes: active_entities: >- {% from 'infrastructure_health.jinja' import dashboard_active_entities %} @@ -288,7 +288,7 @@ template: severity: >- {% from 'infrastructure_health.jinja' import dashboard_severity %} {{ (dashboard_severity | as_function)() }} - count_policy: "Maximum of active health sources and Repairs; overlapping Repairs are not added twice." + count_policy: "Scheduled jobs count individually; other health sources count once. Maximum with Repairs avoids adding overlapping Repairs twice." - binary_sensor: - name: "Home Water Problem"