From 33a2a09eb5936ac16d2bac6a0fe37ba5d5c4b64c Mon Sep 17 00:00:00 2001 From: Carlo Costanzo Date: Fri, 17 Jul 2026 17:06:00 -0400 Subject: [PATCH] Refine dashboard entry points and activity views --- config/dashboards/README.md | 1 + .../partials/home_sections.yaml | 68 ++-- .../partials/home_water_sections.yaml | 136 +++++-- .../partials/joanna_activity_sections.yaml | 354 ++++++++++++++++++ .../partials/joanna_sections.yaml | 4 +- .../views/06a_joanna_activity.yaml | 22 ++ .../partials/entry_points_sections.yaml | 290 ++++++++++++++ .../overview/sections/00_summary.yaml | 20 +- .../dashboards/overview/views/03_windows.yaml | 23 ++ config/packages/bearclaw.yaml | 2 + 10 files changed, 862 insertions(+), 58 deletions(-) create mode 100644 config/dashboards/infrastructure/partials/joanna_activity_sections.yaml create mode 100644 config/dashboards/infrastructure/views/06a_joanna_activity.yaml create mode 100644 config/dashboards/overview/partials/entry_points_sections.yaml create mode 100644 config/dashboards/overview/views/03_windows.yaml diff --git a/config/dashboards/README.md b/config/dashboards/README.md index a146f49a..e1e89a5d 100644 --- a/config/dashboards/README.md +++ b/config/dashboards/README.md @@ -61,6 +61,7 @@ This folder is referenced from `config/configuration.yaml` via: - Default Overview YAML dashboard: `lovelace.dashboards.lovelace.filename: ui-lovelace.yaml` - Additional YAML dashboards: `filename: dashboards//dashboard.yaml` - Overview Home uses ordered production sections under `overview/sections/`; approved pilot edits now apply directly there. + - Overview Home's **Windows** ribbon opens a dedicated GPIO entry-point subview with perimeter, MQTT, and individual contact status. - `dashboard-infrastructure` is presented as **Systems** while retaining its stable URL; it owns Home Water, Vacuum, network, compute, storage, and service drill-downs, including hidden WAN/Pi-hole/website detail views. Note: diff --git a/config/dashboards/infrastructure/partials/home_sections.yaml b/config/dashboards/infrastructure/partials/home_sections.yaml index f939fa68..0cd1e4c4 100644 --- a/config/dashboards/infrastructure/partials/home_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_sections.yaml @@ -6,7 +6,8 @@ # Systems Partial - operations summary # House-system health strip, active issues, datacenter, and network trends. # ------------------------------------------------------------------- -# Notes: Infrastructure, Water, and Vacuum provide the operational roll-up for Systems. +# Notes: The health ribbon mirrors the visible dashboard navigation order and labels. +# Notes: DNS and external-monitoring faults roll up under Network instead of duplicating it. # Notes: All overview trends use a 24-hour window with visible series labels. ###################################################################### @@ -35,7 +36,7 @@ - type: custom:button-card template: bearstone_infra_status_kpi entity: sensor.infra_dashboard_active_issue_count - name: Infrastructure + name: Systems icon: mdi:shield-check-outline variables: ok_state: '0' @@ -44,30 +45,33 @@ - type: custom:button-card template: bearstone_infra_status_kpi - entity: binary_sensor.infra_wan_quality_degraded - name: Network - icon: mdi:wan + entity: binary_sensor.garage_phyn_leak_alert + name: Home Water + icon: mdi:shield-water-outline variables: ok_state: 'off' + healthy_text: CLEAR + alert_text: LEAK tap_action: action: navigate - navigation_path: /dashboard-infrastructure/network + navigation_path: /dashboard-infrastructure/home-water - type: custom:button-card template: bearstone_infra_status_kpi - entity: binary_sensor.infra_dns_pihole_degraded - name: DNS - icon: mdi:dns-outline + entity: sensor.l10s_vacuum_error + name: Vacuum + icon: mdi:robot-vacuum variables: - ok_state: 'off' + ok_state: no_error + healthy_text: READY tap_action: action: navigate - navigation_path: /dashboard-infrastructure/network + navigation_path: /dashboard-infrastructure/vacuum - type: custom:button-card template: bearstone_infra_status_kpi entity: binary_sensor.infra_compute_degraded - name: Compute + name: ProxMox icon: mdi:server-network variables: ok_state: 'off' @@ -77,17 +81,20 @@ - type: custom:button-card template: bearstone_infra_status_kpi - entity: binary_sensor.infra_storage_degraded - name: Storage - icon: mdi:database-check-outline + entity: binary_sensor.docker_container_telemetry_degraded + name: Docker + icon: mdi:docker variables: ok_state: 'off' + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/docker - type: custom:button-card template: bearstone_infra_status_kpi - entity: binary_sensor.infra_external_monitoring_degraded - name: External - icon: mdi:web-check + entity: binary_sensor.infra_wan_quality_degraded + name: Network + icon: mdi:wan variables: ok_state: 'off' tap_action: @@ -96,28 +103,27 @@ - type: custom:button-card template: bearstone_infra_status_kpi - entity: binary_sensor.garage_phyn_leak_alert - name: Water - icon: mdi:shield-water-outline + entity: sensor.mariadb_status + name: MariaDB + icon: mdi:database variables: - ok_state: 'off' - healthy_text: CLEAR - alert_text: LEAK + ok_state: running + healthy_text: RUNNING tap_action: action: navigate - navigation_path: /dashboard-infrastructure/home-water + navigation_path: /dashboard-infrastructure/mariadb - type: custom:button-card template: bearstone_infra_status_kpi - entity: sensor.l10s_vacuum_error - name: Vacuum - icon: mdi:robot-vacuum + entity: sensor.bearclaw_status_telemetry + name: Joanna + icon: mdi:robot-outline variables: - ok_state: no_error - healthy_text: READY + ok_state: ok + healthy_text: HEALTHY tap_action: action: navigate - navigation_path: /dashboard-infrastructure/vacuum + navigation_path: /dashboard-infrastructure/joanna # ------------------------------------------------------------------- # Active exceptions and datacenter diff --git a/config/dashboards/infrastructure/partials/home_water_sections.yaml b/config/dashboards/infrastructure/partials/home_water_sections.yaml index 7828f8f5..140317e2 100644 --- a/config/dashboards/infrastructure/partials/home_water_sections.yaml +++ b/config/dashboards/infrastructure/partials/home_water_sections.yaml @@ -10,10 +10,12 @@ # Notes: Maintenance rows reuse existing reset scripts through safe more-info flows. # Notes: Active Phyn leak alerts are shown; the integration's stale latest-test leak flag is not an operator alert. # Notes: Daily usage uses change on the same sum-only statistic configured in Energy. +# Notes: The overview ribbon prioritizes the next maintenance action and frequent controls without hidden grid placeholders. +# Notes: Refrigerator temperature remains a temporary Home Maintenance overview item until a better permanent view is chosen. ###################################################################### # ------------------------------------------------------------------- -# Water health strip (mandatory first full-width wrapper) +# Water overview ribbon (mandatory first full-width wrapper) # ------------------------------------------------------------------- - type: grid column_span: 4 @@ -25,7 +27,7 @@ columns: full layout_type: custom:grid-layout layout: - grid-template-columns: repeat(6, minmax(0, 1fr)) + grid-template-columns: repeat(7, minmax(0, 1fr)) grid-auto-flow: row grid-auto-rows: min-content grid-gap: 12px @@ -35,40 +37,109 @@ grid-template-columns: repeat(2, minmax(0, 1fr)) cards: - type: custom:button-card - template: bearstone_infra_status_kpi - entity: switch.phyn_autoshutoff_enabled - name: Auto Shutoff - icon: mdi:shield-water-outline - variables: - ok_state: 'on' - healthy_text: ACTIVE - alert_text: 'OFF' - - - type: custom:button-card - template: bearstone_infra_status_kpi - entity: binary_sensor.garage_phyn_leak_alert - name: Active Leak - icon: mdi:pipe-leak + template: bearstone_infra_kpi + entity: sensor.downstairs_ac_runtime_since_last_filter_change + name: Next Maintenance + icon: mdi:calendar-clock variables: - ok_state: 'off' - healthy_text: CLEAR - alert_text: LEAK + next_maintenance: > + [[[ + const candidates = []; + const addCandidate = (entityId, label, current, limit, unit) => { + if (!Number.isFinite(current) || !Number.isFinite(limit) || limit <= 0) return; + const remaining = Math.ceil(limit - current); + candidates.push({ + entity: entityId, + progress: current / limit, + text: remaining <= 0 ? `DUE · ${label}` : `${label} · ${remaining}${unit}`, + }); + }; + + addCandidate( + 'sensor.downstairs_ac_runtime_since_last_filter_change', + 'DOWNSTAIRS', + Number(states['sensor.downstairs_ac_runtime_since_last_filter_change']?.state), + 350, + 'H', + ); + addCandidate( + 'sensor.upstairs_ac_runtime_since_last_filter_change', + 'UPSTAIRS', + Number(states['sensor.upstairs_ac_runtime_since_last_filter_change']?.state), + 450, + 'H', + ); + addCandidate( + 'sensor.hvac_condenser_lines_days_since_cleaned', + 'CONDENSER', + Number(states['sensor.hvac_condenser_lines_days_since_cleaned']?.state), + 360, + 'D', + ); + addCandidate( + 'sensor.water_softener_salt_days_since_last_add', + 'SALT', + Number(states['sensor.water_softener_salt_days_since_last_add']?.state), + Number(states['sensor.water_softener_salt_average_days_between_refills']?.state), + 'D', + ); + + candidates.sort((a, b) => b.progress - a.progress); + return candidates[0] ?? { entity: entity.entity_id, text: 'NO DATA' }; + ]]] + state_display: > + [[[ + const text = variables?.next_maintenance?.text ?? 'NO DATA'; + return text + .replace('DOWNSTAIRS', 'DOWN') + .replace('UPSTAIRS', 'UP') + .replace('CONDENSER', 'COND'); + ]]] + tap_action: + action: more-info + entity: > + [[[ + return variables?.next_maintenance?.entity ?? entity.entity_id; + ]]] - type: custom:button-card template: bearstone_infra_status_kpi entity: valve.phyn_shutoff_valve - name: Main Valve + name: Water Protection icon: mdi:valve variables: ok_state: open healthy_text: OPEN alert_text: CLOSED + tap_action: + action: more-info + + - type: custom:button-card + template: bearstone_infra_kpi + entity: sensor.rachio_controller_status + name: Sprinklers + icon: mdi:sprinkler-variant + state_display: > + [[[ + const status = String(entity?.state ?? '').toLowerCase(); + if (status === 'watering') { + const zones = String(entity?.attributes?.active_zones ?? '').trim(); + return zones ? `RUNNING · ${zones.toUpperCase()}` : 'RUNNING'; + } + if (status === 'rain_delay') return 'RAIN DELAY'; + if (status === 'standby') return 'STANDBY'; + if (['unknown', 'unavailable', 'none', ''].includes(status)) return 'OFFLINE'; + return 'READY'; + ]]] + tap_action: + action: navigate + navigation_path: /dashboard-infrastructure/sprinklers - type: custom:button-card template: bearstone_infra_kpi - entity: sensor.phyn_average_water_flow_rate - name: Flow Rate - icon: mdi:waves-arrow-right + entity: sensor.blink_blink1_temperature + name: Refrigerator + icon: mdi:fridge-outline - type: custom:button-card template: bearstone_infra_kpi @@ -82,6 +153,25 @@ name: Used Today icon: mdi:water-outline + - type: custom:button-card + template: bearstone_infra_kpi + entity: input_datetime.water_delivery_date + name: Next Delivery + icon: mdi:truck-delivery-outline + state_display: > + [[[ + const raw = String(entity?.state ?? ''); + const value = raw ? new Date(`${raw}T00:00:00`) : null; + if (!(value instanceof Date) || Number.isNaN(value.getTime())) return 'NO DATE'; + const today = new Date(); + today.setHours(0, 0, 0, 0); + const formatted = value.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }).toUpperCase(); + return value < today ? `OVERDUE · ${formatted}` : formatted; + ]]] + tap_action: + action: url + url_path: https://www.readyrefresh.com/en/login + # ------------------------------------------------------------------- # Home maintenance and outdoor water # ------------------------------------------------------------------- diff --git a/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml b/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml new file mode 100644 index 00000000..3a16406e --- /dev/null +++ b/config/dashboards/infrastructure/partials/joanna_activity_sections.yaml @@ -0,0 +1,354 @@ +###################################################################### +# @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 ea34c5d6..88c07f67 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 large embedded logbook is replaced by a direct Activity navigation card. +# Notes: The activity drill-down reads the same rolling 24-hour runtime job history as the dispatch counters. # Notes: This view changes HA presentation only; BearClaw runtime behavior is untouched. ###################################################################### @@ -116,7 +116,7 @@ state_display: '[[[ return `${entity.state} dispatches in 24h`; ]]]' tap_action: action: navigate - navigation_path: /logbook?entity_id=script.joanna_dispatch + navigation_path: /dashboard-infrastructure/joanna-activity - type: custom:vertical-stack-in-card card_mod: diff --git a/config/dashboards/infrastructure/views/06a_joanna_activity.yaml b/config/dashboards/infrastructure/views/06a_joanna_activity.yaml new file mode 100644 index 00000000..6d2a507f --- /dev/null +++ b/config/dashboards/infrastructure/views/06a_joanna_activity.yaml @@ -0,0 +1,22 @@ +###################################################################### +# @CCOSTAN - Follow Me on X +# 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. +# ------------------------------------------------------------------- +# Notes: Hidden drill-down reached from the Joanna Dispatch Trend card. +###################################################################### + +title: Joanna Activity +path: joanna-activity +type: sections +subview: true +back_path: /dashboard-infrastructure/joanna +theme: default +max_columns: 4 +dense_section_placement: true +badges: [] +sections: !include /config/dashboards/infrastructure/partials/joanna_activity_sections.yaml +cards: [] diff --git a/config/dashboards/overview/partials/entry_points_sections.yaml b/config/dashboards/overview/partials/entry_points_sections.yaml new file mode 100644 index 00000000..cfa36ad0 --- /dev/null +++ b/config/dashboards/overview/partials/entry_points_sections.yaml @@ -0,0 +1,290 @@ +###################################################################### +# @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 - GPIO entry points +# Related Issue: 1843 +# Perimeter summary and all MQTT-backed entry-point contacts. +# ------------------------------------------------------------------- +# Notes: Contact tiles follow group.entry_points and open more-info history. +# Notes: MQTT health is global; per-NodeMCU freshness is not currently exposed. +###################################################################### + +# ------------------------------------------------------------------- +# Perimeter summary (mandatory first full-width wrapper) +# ------------------------------------------------------------------- +- 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 + align-items: stretch + grid-gap: 10px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Perimeter Summary + view_layout: + grid-column: 1 / -1 + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: group.entry_points + name: GPIO Contacts + icon: mdi:window-closed-variant + variables: + ok_state: 'off' + healthy_text: SECURE + alert_text: OPEN + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mqtt_broker_problem + name: MQTT Broker + icon: mdi:message-check-outline + variables: + ok_state: 'off' + healthy_text: CONNECTED + alert_text: PROBLEM + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: lock.front_door + name: Front Lock + icon: mdi:door-closed-lock + variables: + ok_state: locked + healthy_text: LOCKED + alert_text: UNLOCKED + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: group.garage_doors + name: Garage Doors + icon: mdi:garage-variant + variables: + ok_state: closed + healthy_text: CLOSED + alert_text: OPEN + +# ------------------------------------------------------------------- +# Entry doors and sliders +# ------------------------------------------------------------------- +- 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 + align-items: stretch + grid-gap: 10px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Entry Doors and Sliders + view_layout: + grid-column: 1 / -1 + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio5 + name: Front Door + icon: mdi:door-closed + variables: &contact_status + ok_state: 'off' + healthy_text: CLOSED + alert_text: OPEN + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio12 + name: Interior Garage Door + icon: mdi:door-closed + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio5 + name: Back Door + icon: mdi:door-closed + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio9 + name: Master Slider + icon: mdi:door-sliding + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio12 + name: Main Slider + icon: mdi:door-sliding + variables: *contact_status + +# ------------------------------------------------------------------- +# Common-area windows +# ------------------------------------------------------------------- +- 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 + align-items: stretch + grid-gap: 10px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Common-Area Windows + view_layout: + grid-column: 1 / -1 + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio10 + name: Dining Room Windows + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio13 + name: Front Den Windows + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio14 + name: Office Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio4 + name: Dinette Windows + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio10 + name: Master Bathroom Window + icon: mdi:window-closed-variant + variables: *contact_status + +# ------------------------------------------------------------------- +# Bedroom windows +# ------------------------------------------------------------------- +- 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 + align-items: stretch + grid-gap: 10px + margin: 0 + mediaquery: + "(max-width: 900px)": + grid-template-columns: repeat(2, minmax(0, 1fr)) + cards: + - type: custom:button-card + template: bearstone_infra_panel_header + name: Bedroom Windows + view_layout: + grid-column: 1 / -1 + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu1_gpio4 + name: Paige Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio13 + name: Upstairs Bedroom Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu2_gpio14 + name: Justin Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu3_gpio4 + name: Stacey Bedroom Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu3_gpio5 + name: Carlo Bedroom Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu3_gpio10 + name: Master Bedroom Window + icon: mdi:window-closed-variant + variables: *contact_status + + - type: custom:button-card + template: bearstone_infra_status_kpi + entity: binary_sensor.mcu3_gpio14 + name: Upstairs Windows + icon: mdi:window-closed-variant + variables: *contact_status diff --git a/config/dashboards/overview/sections/00_summary.yaml b/config/dashboards/overview/sections/00_summary.yaml index f34094f4..fe6ba318 100644 --- a/config/dashboards/overview/sections/00_summary.yaml +++ b/config/dashboards/overview/sections/00_summary.yaml @@ -4,7 +4,8 @@ # Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig # ------------------------------------------------------------------- # Overview Section - priority ribbon and daily controls -# Systems first, then frequent doors, climate, and vacuum access. +# Related Issue: 1843 +# Systems first, then frequent entry points, climate, and vacuum access. # ------------------------------------------------------------------- # Notes: Equal-width pills keep the whole ribbon compact and consistent. ###################################################################### @@ -19,7 +20,7 @@ cards: columns: full layout_type: custom:grid-layout layout: - grid-template-columns: repeat(6, minmax(0, 1fr)) + grid-template-columns: repeat(7, minmax(0, 1fr)) grid-auto-flow: row grid-auto-rows: min-content align-items: center @@ -65,6 +66,21 @@ cards: ok_states: - locked + - type: custom:button-card + template: bearstone_overview_ribbon_control + entity: group.entry_points + name: Windows + icon: mdi:window-closed-variant + variables: + ok_states: + - 'off' + state_labels: + 'off': SECURE + 'on': OPEN + tap_action: + action: navigate + navigation_path: /lovelace/windows + - type: custom:button-card template: bearstone_overview_ribbon_control entity: climate.downstairs diff --git a/config/dashboards/overview/views/03_windows.yaml b/config/dashboards/overview/views/03_windows.yaml new file mode 100644 index 00000000..52f470d9 --- /dev/null +++ b/config/dashboards/overview/views/03_windows.yaml @@ -0,0 +1,23 @@ +###################################################################### +# @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 - GPIO entry points +# Related Issue: 1843 +# Window, door, and slider contacts with perimeter and MQTT context. +# ------------------------------------------------------------------- +# Notes: The Home Windows ribbon is the canonical entry point for this subview. +###################################################################### + +title: Windows +path: windows +subview: true +icon: mdi:window-closed-variant +theme: Backend-selected +type: sections +max_columns: 4 +dense_section_placement: true +badges: [] +sections: !include /config/dashboards/overview/partials/entry_points_sections.yaml +cards: [] diff --git a/config/packages/bearclaw.yaml b/config/packages/bearclaw.yaml index 34b29da9..292d83b7 100644 --- a/config/packages/bearclaw.yaml +++ b/config/packages/bearclaw.yaml @@ -13,6 +13,7 @@ # Notes: Shared script helper `script.joanna_dispatch` lives in config/script/joanna_dispatch.yaml. # Notes: The callback webhook writes JOANNA activity entries to logbook for traceability and optional HA alerts. # 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: Codex reset-credit expiry telemetry is sourced from the appliance `codexUsage` status field. # Notes: Codex reset walkthrough: https://youtu.be/7wKhtrvtyiI # Notes: Codex reset companion post: https://www.vcloudinfo.com/2026/06/track-codex-resets-home-assistant.html @@ -134,6 +135,7 @@ sensor: value_template: "{{ 'ok' if value_json.ok | default(false) else 'error' }}" json_attributes: - dispatchStats + - dispatchActivity - queue - active - platform