Refine infrastructure dashboard layout

master
Carlo Costanzo 6 days ago
parent 8560a6d0b7
commit c81edbe846

@ -78,6 +78,10 @@ Lovelace resources are loaded from:
- Include paths in Lovelace YAML should use absolute container paths starting with `/config/`.
- Example: `!include /config/dashboards/overview/partials/some_cards.yaml`
- Views are loaded using `!include_dir_list` and ordered by filename (prefix with `01_`, `02_`, etc.).
- Infrastructure views are desktop-first and dense by default: use `type: sections`, `max_columns: 4`, and `dense_section_placement: true` on every standard view.
- Use responsive `layout-card` grids with `min-content` rows inside full-width section wrappers so panels pack tightly without fixed-height gaps.
- Keep related panels similar in height, avoid spacer cards, and verify both desktop and single-column mobile layouts.
- Keep WAN telemetry, compact dual Pi-hole controls, and current website health together on the Network view; reserve hidden subviews for detailed Pi-hole analytics and domain/monitor telemetry.
- Prefer card-native styling; treat `card_mod` as a last resort.
- Prefer CSS variables (`var(--*)`) over hardcoded hex colors.
- When using the Stitch MCP for inspiration, generation may exceed the tool timeout; wait and then fetch results via `stitch/list_screens` and `stitch/get_screen`.

@ -8,6 +8,7 @@
# -------------------------------------------------------------------
# Notes: Entry point for dashboard key `dashboard-infrastructure`. Views are loaded from /config/dashboards/infrastructure/views.
# Notes: Joanna dispatch telemetry view is available at `/dashboard-infrastructure/joanna`.
# Notes: Network combines WAN, dual Pi-hole, and website health; `/pihole` and `/website-health` are detail subviews.
######################################################################
button_card_templates: !include /config/dashboards/infrastructure/templates/button_card_templates.yaml

@ -3,147 +3,163 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - Docker containers sections
# Related Issue: 1560, 1584, 1725
# Sections layout for the Docker containers view.
# Infrastructure Partial - Docker operations
# Host health, updates, volumes, active exceptions, and inventory drill-down.
# -------------------------------------------------------------------
# Notes: Auto-discovers Portainer container entities from `switch.*_container`.
# Notes: Keeps cards visible when Portainer telemetry is unavailable (degraded mode).
# Notes: Includes stack-level status tiles from Portainer `*_stack_status` entities.
# Notes: Portainer volume usage is visible; volume prune is confined to host maintenance popups with hold-confirm actions.
# Notes: Docker volume summary tiles are explicit per host because Portainer does not expose endpoint volume totals for every host.
# Notes: Excludes transient codex_appliance Portainer run containers from generic container lists.
# Notes: Healthy stacks and containers live in the hidden Docker Inventory subview.
# Notes: Host maintenance controls remain in the existing confirmed popups.
######################################################################
# -------------------------------------------------------------------
# Docker health strip (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
columns: 1
square: false
cards:
- type: custom:layout-card
grid_options:
columns: full
layout_type: custom:grid-layout
layout:
grid-template-columns: repeat(5, 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.garage_ups_load
name: UPS Load
icon: mdi:battery-charging-60
entity: sensor.docker_monitored_container_count
name: Monitored
icon: mdi:docker
state_display: >
[[[
const ids = states['sensor.docker_monitored_switch_inventory']?.attributes?.entity_id;
const unique = Array.isArray(ids)
? ids.filter(id => !String(id).endsWith('_container_2')).length
: entity.state;
return `${unique} workloads`;
]]]
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: sensor.docker_monitored_unavailable_count
name: Unavailable
icon: mdi:alert-circle-outline
variables:
ok_state: '0'
healthy_text: NONE
raw_suffix: UNAVAILABLE
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.docker_container_telemetry_degraded
name: Telemetry
icon: mdi:lan-check
variables:
ok_state: 'off'
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.carlo_nvr_volume_1_volume_used
name: NVR Used
icon: mdi:harddisk
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.carlo_nas01_volume_1_volume_used
name: NAS Used
icon: mdi:nas
# -------------------------------------------------------------------
# Host maintenance signals and volume telemetry
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 4
columns: 1
square: false
cards:
- type: custom:bubble-card
card_type: button
button_type: state
- 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: Host Updates
- type: custom:button-card
template: bearstone_infra_update_row
entity: sensor.docker_10_apt_status
name: docker_10
icon: mdi:server
show_state: true
show_last_changed: true
card_layout: large
tap_action:
action: navigate
navigation_path: '#infra-docker-10'
button_action:
tap_action:
action: navigate
navigation_path: '#infra-docker-10'
- type: custom:bubble-card
card_type: button
button_type: state
- type: custom:button-card
template: bearstone_infra_update_row
entity: sensor.docker_17_apt_status
name: docker_17
icon: mdi:server
show_state: true
show_last_changed: true
card_layout: large
tap_action:
action: navigate
navigation_path: '#infra-docker-17'
button_action:
tap_action:
action: navigate
navigation_path: '#infra-docker-17'
- type: custom:bubble-card
card_type: button
button_type: state
- type: custom:button-card
template: bearstone_infra_update_row
entity: sensor.docker_69_apt_status
name: docker_69
icon: mdi:server
show_state: true
show_last_changed: true
card_layout: large
tap_action:
action: navigate
navigation_path: '#infra-docker-69'
button_action:
tap_action:
action: navigate
navigation_path: '#infra-docker-69'
- type: custom:bubble-card
card_type: button
button_type: state
- type: custom:button-card
template: bearstone_infra_update_row
entity: sensor.docker_14_apt_status
name: docker_14
icon: mdi:server
show_state: true
show_last_changed: true
card_layout: large
tap_action:
action: navigate
navigation_path: '#infra-docker-14'
button_action:
tap_action:
action: navigate
navigation_path: '#infra-docker-14'
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Docker Volumes
- 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_docker_volume_row
entity: sensor.carlo_hass_volume_disk_usage_total_size
name: docker_10
variables:
total_entity: sensor.carlo_hass_volume_disk_usage_total_size
- type: custom:button-card
template: bearstone_infra_docker_volume_row
entity: button.docker2_prune_unused_volumes
name: docker_14
variables:
not_instrumented: true
- type: custom:button-card
template: bearstone_infra_docker_volume_row
entity: button.docker17_prune_unused_volumes
@ -164,6 +180,7 @@
- sensor.openrecord_local_openrecord_next_cache_volume_size
- sensor.openrecord_local_openrecord_scrapers_node_modules_volume_size
- sensor.openrecord_local_openrecord_web_node_modules_volume_size
- type: custom:button-card
template: bearstone_infra_docker_volume_row
entity: sensor.docker69_volume_disk_usage_total_size
@ -171,6 +188,9 @@
variables:
total_entity: sensor.docker69_volume_disk_usage_total_size
# -------------------------------------------------------------------
# Exceptions only; full inventory is a hidden subview
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 1
@ -184,9 +204,27 @@
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Docker Stacks
name: Workload Exceptions
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.docker_container_telemetry_degraded
name: Docker telemetry degraded
icon: mdi:lan-disconnect
variables:
alert_kind: binary_on
state_display: >
[[[
const unavailable = states['sensor.docker_monitored_unavailable_count']?.state ?? '0';
const ids = states['sensor.docker_monitored_switch_inventory']?.attributes?.entity_id;
const total = Array.isArray(ids)
? ids.filter(id => !String(id).endsWith('_container_2')).length
: (states['sensor.docker_monitored_container_count']?.state ?? '0');
return `${unavailable}/${total} unavailable`;
]]]
- type: custom:auto-entities
show_empty: true
show_empty: false
card:
type: custom:layout-card
layout_type: custom:grid-layout
@ -203,84 +241,67 @@
filter:
include:
- entity_id: binary_sensor.*_stack_status
state: 'off'
options:
type: custom:button-card
template: bearstone_infra_list_row_running
icon: mdi:package-variant
state_display: >
[[[
const base = entity.entity_id.replace('binary_sensor.', '').replace('_stack_status', '');
const count = states[`sensor.${base}_stack_containers_count`]?.state ?? 'n/a';
const type = String(states[`sensor.${base}_stack_type`]?.state ?? 'n/a').toUpperCase();
const status = entity.state === 'on' ? 'RUNNING' : 'DOWN';
return `${status} | ${count} ctrs | ${type}`;
]]]
icon: mdi:package-variant-closed-remove
state_display: '[[[ return "STACK DOWN"; ]]]'
sort:
method: name
- type: grid
column_span: 4
columns: 1
square: false
cards:
- !include /config/dashboards/infrastructure/popups/docker_10_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_17_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_69_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_14_maintenance.yaml
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.docker_container_telemetry_degraded
name: Docker telemetry degraded
icon: mdi:lan-disconnect
variables:
alert_kind: binary_on
state_display: >-
[[[
const unavailable = states['sensor.docker_monitored_unavailable_count']?.state ?? '0';
const total = states['sensor.docker_monitored_container_count']?.state ?? '0';
return `${unavailable}/${total} unavailable`;
]]]
- type: custom:auto-entities
show_empty: true
grid_options:
columns: full
show_empty: false
card:
type: custom:layout-card
layout_type: custom:grid-layout
layout:
grid-template-columns: repeat(4, minmax(0, 1fr))
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(2, minmax(0, 1fr))
grid-template-columns: repeat(1, minmax(0, 1fr))
card_param: cards
filter:
include:
- entity_id: "/^switch\\..*_container(_2)?$/"
options:
type: custom:button-card
template: bearstone_infra_container_row
icon: mdi:docker
exclude:
- entity_id: "/^switch\\.[0-9a-f]{12}_codex_appliance_container$/"
- entity_id: "/^switch\\.docker_files_codex_appliance_run_[0-9a-f]+_container$/"
- state: unavailable
- state: unknown
template: |
[{% for entity_id in state_attr('sensor.docker_monitored_switch_inventory', 'entity_id') or [] %}
{% if not entity_id.endswith('_container_2') and states(entity_id) == 'off' %}
{'entity': '{{ entity_id }}',
'type': 'custom:button-card',
'template': 'bearstone_infra_container_row',
'icon': 'mdi:docker'},
{% endif %}
{% endfor %}]
sort:
method: name
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.docker_monitored_container_count
name: Open Full Docker Inventory
icon: mdi:format-list-bulleted-square
state_display: >
[[[
const ids = states['sensor.docker_monitored_switch_inventory']?.attributes?.entity_id;
const unique = Array.isArray(ids)
? ids.filter(id => !String(id).endsWith('_container_2')).length
: entity.state;
return `${unique} monitored workloads`;
]]]
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/docker-inventory
# Existing confirmed host maintenance popups are intentionally reused.
- type: grid
column_span: 4
columns: 1
square: false
cards:
- !include /config/dashboards/infrastructure/popups/docker_10_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_17_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_69_maintenance.yaml
- !include /config/dashboards/infrastructure/popups/docker_14_maintenance.yaml

@ -0,0 +1,175 @@
######################################################################
# @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 - Docker inventory
# Complete stack and monitored-container inventory for drill-down use.
# -------------------------------------------------------------------
# Notes: This partial belongs to a hidden subview; the main Docker view stays exception-first.
######################################################################
# -------------------------------------------------------------------
# Inventory navigation (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(3, 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:button-card
template: bearstone_infra_list_row
entity: sensor.docker_monitored_container_count
name: Back to Docker Operations
icon: mdi:arrow-left
state_display: >
[[[
const ids = states['sensor.docker_monitored_switch_inventory']?.attributes?.entity_id;
const unique = Array.isArray(ids)
? ids.filter(id => !String(id).endsWith('_container_2')).length
: entity.state;
return `${unique} monitored workloads`;
]]]
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/docker
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: sensor.docker_monitored_unavailable_count
name: Unavailable
icon: mdi:alert-circle-outline
variables:
ok_state: '0'
healthy_text: NONE
raw_suffix: UNAVAILABLE
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.docker_container_telemetry_degraded
name: Telemetry
icon: mdi:lan-check
variables:
ok_state: 'off'
# -------------------------------------------------------------------
# All stacks
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Docker Stacks
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.docker_monitored_container_count
name: Stack Telemetry
icon: mdi:package-variant
state_display: >
[[[
const count = Object.keys(states)
.filter(id => /^binary_sensor\..*_stack_status$/.test(id)).length;
return count ? `${count} stacks reported` : 'Not exposed by Portainer';
]]]
- type: custom:auto-entities
show_empty: false
card:
type: custom:layout-card
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))
card_param: cards
filter:
include:
- entity_id: binary_sensor.*_stack_status
options:
type: custom:button-card
template: bearstone_infra_list_row_running
icon: mdi:package-variant
state_display: >
[[[
const base = entity.entity_id.replace('binary_sensor.', '').replace('_stack_status', '');
const count = states[`sensor.${base}_stack_containers_count`]?.state ?? 'n/a';
const status = entity.state === 'on' ? 'RUNNING' : 'DOWN';
return `${status} | ${count} containers`;
]]]
sort:
method: name
# -------------------------------------------------------------------
# All monitored containers
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Monitored Containers
- type: custom:auto-entities
show_empty: true
card:
type: custom:layout-card
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))
card_param: cards
filter:
template: |
[{% for entity_id in state_attr('sensor.docker_monitored_switch_inventory', 'entity_id') or [] %}
{% if not entity_id.endswith('_container_2') %}
{'entity': '{{ entity_id }}',
'type': 'custom:button-card',
'template': 'bearstone_infra_container_row',
'icon': 'mdi:docker'},
{% endif %}
{% endfor %}]
sort:
method: name

@ -3,16 +3,15 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - Home sections
# Desktop-first infra overview: full-width hero, exceptions-first alerts.
# Infrastructure Partial - operations summary
# Exception-first health strip, active issues, datacenter, and network trends.
# -------------------------------------------------------------------
# Notes: Default/light theme only; no dark-mode specific styling.
# Notes: Keep the first section full-width to avoid whitespace on desktop.
# Notes: Excludes transient codex_appliance Portainer run containers from generic exception lists.
# Notes: Six category tiles replace the mostly empty production status panel.
# Notes: All overview trends use a 24-hour window with visible series labels.
######################################################################
# -------------------------------------------------------------------
# Home hero (mandatory first full-width container)
# Category health strip (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
@ -24,423 +23,302 @@
columns: full
layout_type: custom:grid-layout
layout:
grid-template-columns: repeat(2, minmax(0, 1fr))
grid-template-columns: repeat(6, 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
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
grid-template-columns: repeat(2, minmax(0, 1fr))
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Infrastructure Status
template: bearstone_infra_status_kpi
entity: sensor.infra_dashboard_active_issue_count
name: Overall
icon: mdi:shield-check-outline
variables:
ok_state: '0'
healthy_text: ALL CLEAR
raw_suffix: ACTIVE
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.joanna_ha_dispatches_24h
name: Joanna Dispatch
icon: mdi:robot
template: bearstone_infra_status_kpi
entity: binary_sensor.infra_wan_quality_degraded
name: Network
icon: mdi:wan
variables:
ok_state: 'off'
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/joanna
state_display: >
[[[
const ha24 = states['sensor.joanna_ha_dispatches_24h']?.state ?? '0';
const all24 = states['sensor.joanna_all_dispatches_24h']?.state ?? '0';
const queue = states['sensor.joanna_queue_pending']?.state ?? '0';
return `HA ${ha24} | All ${all24} | Q ${queue}`;
]]]
navigation_path: /dashboard-infrastructure/network
- type: custom:button-card
template: bearstone_infra_list_row
name: Network
icon: mdi:access-point-network
show_state: false
template: bearstone_infra_status_kpi
entity: binary_sensor.infra_dns_pihole_degraded
name: DNS
icon: mdi:dns-outline
variables:
ok_state: 'off'
tap_action:
action: none
action: navigate
navigation_path: /dashboard-infrastructure/network
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.unifi_ap_office_clients
name: Office AP has 0 clients
icon: mdi:wifi-alert
template: bearstone_infra_status_kpi
entity: binary_sensor.infra_compute_degraded
name: Compute
icon: mdi:server-network
variables:
alert_kind: ap_zero
min_age_s: 300
ok_state: 'off'
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.unifi_ap_study_clients
name: Study AP has 0 clients
icon: mdi:wifi-alert
template: bearstone_infra_status_kpi
entity: binary_sensor.infra_storage_degraded
name: Storage
icon: mdi:database-check-outline
variables:
alert_kind: ap_zero
min_age_s: 300
ok_state: 'off'
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.unifi_ap_garage_clients
name: Garage AP has 0 clients
icon: mdi:wifi-alert
template: bearstone_infra_status_kpi
entity: binary_sensor.infra_external_monitoring_degraded
name: External
icon: mdi:web-check
variables:
alert_kind: ap_zero
min_age_s: 300
ok_state: 'off'
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/network
# -------------------------------------------------------------------
# Active exceptions and datacenter
# -------------------------------------------------------------------
- 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: Active Exceptions
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.speedtest_download
name: Internet speed is slow
icon: mdi:speedometer-slow
variables:
alert_kind: speedtest_slow
threshold: 300
template: bearstone_infra_list_row
entity: sensor.infra_dashboard_active_issue_count
name: Operations Summary
icon: mdi:shield-search
state_display: >
[[[
const dl = parseFloat(states['sensor.speedtest_download']?.state);
const ul = parseFloat(states['sensor.speedtest_upload']?.state);
const dlText = Number.isFinite(dl) ? (dl.toFixed(0) + ' Mbps') : 'n/a';
const ulText = Number.isFinite(ul) ? (ul.toFixed(0) + ' Mbps') : 'n/a';
return `DL ${dlText} | UL ${ulText}`;
const count = Number(entity?.state);
if (!Number.isFinite(count)) return 'NO DATA';
return count === 0 ? 'NO ACTIVE EXCEPTIONS' : `${count} CATEGORIES NEED ATTENTION`;
]]]
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.speedtest_ping
name: WAN latency high
entity: binary_sensor.infra_wan_quality_degraded
name: WAN quality degraded
icon: mdi:wan
variables:
alert_kind: disk_high
threshold: 80
state_display: >
[[[ return `${entity.state} ms`; ]]]
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/network
- type: custom:button-card
template: bearstone_infra_alert_row
entity: switch.pi_hole
name: DNS Pi-hole disabled
entity: binary_sensor.infra_dns_pihole_degraded
name: Pi-hole DNS degraded
icon: mdi:dns-outline
variables:
alert_kind: binary_off
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/pihole
state_display: >
[[[ return `Switch ${entity.state}`; ]]]
navigation_path: /dashboard-infrastructure/network
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.pihole_status
name: DNS Pi-hole service down
icon: mdi:dns
entity: binary_sensor.proxmox1_runtime_healthy
name: Proxmox01 runtime unavailable
icon: mdi:server-off
variables:
alert_kind: binary_off
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/pihole
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.vcloudinfo_com
name: vcloudinfo.com is down
icon: mdi:web-cancel
entity: binary_sensor.proxmox02_runtime_healthy
name: Proxmox02 runtime unavailable
icon: mdi:server-off
variables:
alert_kind: binary_off
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.www_kingcrafthomes_com
name: kingcrafthomes.com is down
icon: mdi:web-cancel
entity: binary_sensor.node_proxmox1_updates_packages
name: Proxmox01 updates pending
icon: mdi:package-up
variables:
alert_kind: binary_off
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.bear_stone
name: Bear Stone is down
icon: mdi:web-cancel
entity: binary_sensor.node_proxmox02_updates_packages
name: Proxmox02 updates pending
icon: mdi:package-up
variables:
alert_kind: binary_off
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
- type: custom:button-card
template: bearstone_infra_list_row
name: Domain expiration critical
icon: mdi:domain-off
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
state_display: >
[[[
const ids = [
'sensor.vcloudinfo_com_days_until_expiration',
'sensor.kingcrafthomes_com_days_until_expiration'
];
let min = null;
ids.forEach((id) => {
const raw = states[id]?.state;
const n = Number(raw);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min === null) ? 'No data' : `${Math.round(min)} days remaining`;
]]]
styles:
card:
- display: >
[[[
const ids = [
'sensor.vcloudinfo_com_days_until_expiration',
'sensor.kingcrafthomes_com_days_until_expiration'
];
let min = null;
ids.forEach((id) => {
const n = Number(states[id]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min !== null && min < 14) ? 'block' : 'none';
]]]
- type: custom:button-card
template: bearstone_infra_list_row
name: Domain expiration warning
icon: mdi:domain
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
state_display: >
[[[
const ids = [
'sensor.vcloudinfo_com_days_until_expiration',
'sensor.kingcrafthomes_com_days_until_expiration'
];
let min = null;
ids.forEach((id) => {
const raw = states[id]?.state;
const n = Number(raw);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min === null) ? 'No data' : `${Math.round(min)} days remaining`;
]]]
styles:
card:
- display: >
[[[
const ids = [
'sensor.vcloudinfo_com_days_until_expiration',
'sensor.kingcrafthomes_com_days_until_expiration'
];
let min = null;
ids.forEach((id) => {
const n = Number(states[id]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min !== null && min >= 14 && min < 30) ? 'block' : 'none';
]]]
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_list_row
name: Certificate expiration critical
icon: mdi:certificate-outline
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
template: bearstone_infra_alert_row
entity: binary_sensor.docker_container_telemetry_degraded
name: Docker telemetry degraded
icon: mdi:docker
variables:
alert_kind: binary_on
state_display: >
[[[
const keys = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
/(vcloudinfo|kingcrafthomes)/.test(k) &&
/(cert|ssl|tls)/.test(k)
);
let min = null;
keys.forEach((k) => {
const n = Number(states[k]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min === null) ? 'Not available' : `${Math.round(min)} days remaining`;
]]]
styles:
card:
- display: >
[[[
const keys = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
/(vcloudinfo|kingcrafthomes)/.test(k) &&
/(cert|ssl|tls)/.test(k)
);
let min = null;
keys.forEach((k) => {
const n = Number(states[k]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min !== null && min < 14) ? 'block' : 'none';
const unavailable = states['sensor.docker_monitored_unavailable_count']?.state ?? '0';
const total = states['sensor.docker_monitored_container_count']?.state ?? '0';
return `${unavailable}/${total} unavailable`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
name: Certificate expiration warning
icon: mdi:certificate
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health
state_display: >
[[[
const keys = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
/(vcloudinfo|kingcrafthomes)/.test(k) &&
/(cert|ssl|tls)/.test(k)
);
let min = null;
keys.forEach((k) => {
const n = Number(states[k]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min === null) ? 'Not available' : `${Math.round(min)} days remaining`;
]]]
styles:
card:
- display: >
[[[
const keys = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
/(vcloudinfo|kingcrafthomes)/.test(k) &&
/(cert|ssl|tls)/.test(k)
);
let min = null;
keys.forEach((k) => {
const n = Number(states[k]?.state);
if (Number.isFinite(n)) min = (min === null) ? n : Math.min(min, n);
});
return (min !== null && min >= 14 && min < 30) ? 'block' : 'none';
]]]
- type: custom:button-card
template: bearstone_infra_list_row
name: System
icon: mdi:alert-circle-outline
show_state: false
tap_action:
action: none
navigation_path: /dashboard-infrastructure/docker
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.node_proxmox1_updates_packages
name: Proxmox01 updates pending
icon: mdi:package-up
entity: binary_sensor.infra_storage_degraded
name: Storage threshold or telemetry problem
icon: mdi:database-alert-outline
variables:
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/proxmox
state_display: >
[[[ return 'UPDATES'; ]]]
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.node_proxmox02_updates_packages
name: Proxmox02 updates pending
icon: mdi:package-up
entity: binary_sensor.infra_website_degraded
name: Website availability degraded
icon: mdi:web-off
variables:
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/proxmox
state_display: >
[[[ return 'UPDATES'; ]]]
navigation_path: /dashboard-infrastructure/network
- type: custom:button-card
template: bearstone_infra_alert_row
entity: sensor.disk_use_percent
name: Home Assistant disk usage high
icon: mdi:harddisk
entity: sensor.proxmox_garage_average_temperature
name: Garage compute temperature high
icon: mdi:thermometer-alert
variables:
alert_kind: disk_high
threshold: 80
alert_kind: numeric_high
threshold: 145
state_display: >
[[[ return `${entity.state}% used`; ]]]
- type: custom:button-card
template: bearstone_infra_list_row
name: Services
icon: mdi:docker
show_state: false
[[[ return `${entity.state} °F`; ]]]
tap_action:
action: none
action: navigate
navigation_path: /dashboard-infrastructure/proxmox
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.docker_container_telemetry_degraded
name: Docker telemetry degraded
icon: mdi:lan-disconnect
variables:
alert_kind: binary_on
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/docker
state_display: >-
[[[
const unavailable = states['sensor.docker_monitored_unavailable_count']?.state ?? '0';
const total = states['sensor.docker_monitored_container_count']?.state ?? '0';
return `${unavailable}/${total} unavailable`;
]]]
template: bearstone_infra_panel_header
name: Network Trends
- type: custom:auto-entities
show_empty: false
card:
type: custom:layout-card
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))
card_param: cards
filter:
include:
- entity_id: "/^switch\\..*_container(_2)?$/"
options:
type: custom:button-card
template: bearstone_infra_container_row
icon: mdi:docker
exclude:
- entity_id: "/^switch\\.[0-9a-f]{12}_codex_appliance_container$/"
- entity_id: "/^switch\\.docker_files_codex_appliance_run_[0-9a-f]+_container$/"
- state: 'on'
- state: unavailable
- state: unknown
- type: custom:mini-graph-card
name: Wi-Fi Clients
icon: mdi:wifi
hours_to_show: 24
points_per_hour: 1
line_width: 2
smoothing: true
show:
legend: true
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.total_wifi_clients
name: Total
show_graph: false
- entity: sensor.unifi_ap_office_clients
name: Office
- entity: sensor.unifi_ap_study_clients
name: Study
- entity: sensor.unifi_ap_garage_clients
name: Garage
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Internet Throughput
icon: mdi:speedometer
hours_to_show: 24
points_per_hour: 1
line_width: 2
smoothing: true
show:
fill: false
legend: true
labels: false
name: true
icon: true
state: false
entities:
- entity: sensor.speedtest_download
name: Download
show_legend_state: true
- entity: sensor.speedtest_upload
name: Upload
show_legend_state: true
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Datacenter
- type: custom:mini-graph-card
name: Garage Temp
name: Garage Compute Temperature
icon: mdi:thermometer
hours_to_show: 96
points_per_hour: 1
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
@ -450,10 +328,10 @@
icon: true
state: true
color_thresholds:
- value: 50
- value: 80
color: '#43a047'
- value: 135
color: '#f39c12'
- value: 120
color: '#d35400'
- value: 145
color: '#c0392b'
entities:
@ -462,20 +340,8 @@
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:layout-card
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:button-card
template: bearstone_infra_list_row
template: bearstone_infra_list_row_running
entity: sensor.garage_ups_status
name: UPS Status
icon: mdi:battery-heart-variant
@ -487,8 +353,8 @@
icon: mdi:battery-70
state_display: >
[[[
const v = Number(entity?.state);
return Number.isFinite(v) ? `${v.toFixed(0)}%` : (entity?.state ?? 'unknown');
const value = Number(entity?.state);
return Number.isFinite(value) ? `${value.toFixed(0)}%` : 'NO DATA';
]]]
- type: custom:button-card
@ -498,11 +364,11 @@
icon: mdi:timer-outline
state_display: >
[[[
const secs = parseInt(entity?.state, 10);
if (!Number.isFinite(secs)) return entity?.state ?? 'unknown';
const hours = Math.floor(secs / 3600);
const mins = Math.floor((secs % 3600) / 60);
return hours > 0 ? `${hours}h ${mins}m` : `${mins}m`;
const seconds = Number(entity?.state);
if (!Number.isFinite(seconds)) return 'NO DATA';
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
return hours > 0 ? `${hours}h ${minutes}m` : `${minutes}m`;
]]]
- type: custom:button-card
@ -512,95 +378,6 @@
icon: mdi:battery-alert-variant-outline
state_display: >
[[[
const s = String(entity?.state || '').toUpperCase();
return s.includes('OB') ? 'Yes' : 'No';
const status = String(entity?.state ?? '').toUpperCase();
return status ? (status.includes('OB') ? 'YES' : 'NO') : 'NO DATA';
]]]
# -------------------------------------------------------------------
# Overviews (desktop: 2 columns, mobile: stack)
# -------------------------------------------------------------------
- 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: Wi-Fi Overview
- type: custom:mini-graph-card
name: Clients
icon: mdi:wifi
hours_to_show: 24
line_width: 2
points_per_hour: 1
smoothing: true
show:
graph: line
legend: false
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.total_wifi_clients
name: Total
show_state: true
show_graph: false
show_line: false
show_points: false
show_fill: false
show_legend: false
- entity: sensor.unifi_ap_office_clients
name: Office AP
show_state: true
- entity: sensor.unifi_ap_study_clients
name: Study AP
show_state: true
- entity: sensor.unifi_ap_garage_clients
name: Garage AP
show_state: true
- 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: Internet Trend
- type: custom:mini-graph-card
name: Speedtest
icon: mdi:speedometer
hours_to_show: 24
points_per_hour: 1
line_width: 2
smoothing: true
show:
fill: false
legend: false
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.speedtest_download
name: Download
- entity: sensor.speedtest_upload
name: Upload

@ -3,58 +3,67 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - Joanna sections
# Joanna/BearClaw dispatch KPI, trend, latest-state, and filtered activity panels.
# Infrastructure Partial - Joanna
# Focused dispatch health, latest activity, rolling volume, and diagnostics.
# -------------------------------------------------------------------
# Notes: Reuses shared bearstone_infra_* templates and standard infra card_mod snippets.
# Notes: The large embedded logbook is replaced by a direct Activity navigation card.
# Notes: This view changes HA presentation only; BearClaw runtime behavior is untouched.
######################################################################
# -------------------------------------------------------------------
# Dispatch health strip (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 6
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_ha_dispatches_24h
name: HA 24H
icon: mdi:calendar-clock
icon: mdi:home-automation
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.joanna_ha_dispatches_7d
name: HA 7D
icon: mdi:calendar-range
entity: sensor.joanna_all_dispatches_24h
name: All 24H
icon: mdi:robot
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.joanna_ha_dispatches_30d
name: HA 30D
icon: mdi:calendar-month
entity: sensor.joanna_ha_success_rate_24h
name: HA Success
icon: mdi:percent-circle
state_display: '[[[ return `${entity.state}%`; ]]]'
- type: custom:button-card
template: bearstone_infra_kpi
template: bearstone_infra_status_kpi
entity: sensor.joanna_ha_errors_24h
name: HA Errors 24H
icon: mdi:alert-circle-outline
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.codex_reset_credits_available
name: Resets
icon: mdi:backup-restore
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.joanna_all_dispatches_24h
name: All 24H
icon: mdi:calendar-clock
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.joanna_all_dispatches_7d
name: All 7D
icon: mdi:calendar-range
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.joanna_all_dispatches_30d
name: All 30D
icon: mdi:calendar-month
variables:
ok_state: '0'
healthy_text: NONE
raw_suffix: ERRORS
# -------------------------------------------------------------------
# Trend, latest state, and reset summary
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
@ -66,9 +75,10 @@
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: HA Dispatch Trend
name: Dispatch Trend
- type: custom:mini-graph-card
name: HA Dispatches (24h Window)
name: HA Dispatches (24h Rolling)
icon: mdi:chart-timeline-variant
hours_to_show: 168
points_per_hour: 1
@ -82,17 +92,40 @@
state: true
entities:
- entity: sensor.joanna_ha_dispatches_24h
name: HA 24h
name: HA Dispatches
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.joanna_ha_dispatches_7d
name: HA Dispatches 7D
icon: mdi:calendar-range
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.joanna_ha_dispatches_30d
name: HA Dispatches 30D
icon: mdi:calendar-month
- 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`; ]]]'
tap_action:
action: navigate
navigation_path: /logbook?entity_id=script.joanna_dispatch
- type: custom:vertical-stack-in-card
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Latest Snapshot
name: Latest Activity
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.bearclaw_status_telemetry
@ -100,12 +133,12 @@
icon: mdi:robot
state_display: >
[[[
const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {};
const latest = stats?.latest?.haAutomation || {};
const source = latest?.source || 'none';
const when = latest?.dispatchedAt || 'n/a';
const latest = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats?.latest?.haAutomation || {};
const source = latest.source || 'none';
const when = latest.dispatchedAt ? String(latest.dispatchedAt).replace('T', ' ').slice(0, 16) : 'n/a';
return `${source} | ${when}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.bearclaw_status_telemetry
@ -113,48 +146,34 @@
icon: mdi:check-circle-outline
state_display: >
[[[
const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {};
const latest = stats?.latest?.haAutomation || {};
const state = latest?.state || 'none';
const when = latest?.completedAt || 'n/a';
const latest = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats?.latest?.haAutomation || {};
const state = latest.state || 'none';
const when = latest.completedAt ? String(latest.completedAt).replace('T', ' ').slice(0, 16) : 'n/a';
return `${state} | ${when}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.bearclaw_status_telemetry
name: Latest Any Dispatch
icon: mdi:robot-excited
state_display: >
[[[
const stats = states['sensor.bearclaw_status_telemetry']?.attributes?.dispatchStats || {};
const latest = stats?.latest?.all || {};
const source = latest?.source || 'none';
const when = latest?.dispatchedAt || 'n/a';
return `${source} | ${when}`;
]]]
entity: sensor.joanna_all_dispatches_7d
name: All Dispatches 7D
icon: mdi:calendar-range
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.bearclaw_status_telemetry
name: HA Success (24h)
icon: mdi:percent-circle
state_display: >
[[[
const v = states['sensor.joanna_ha_success_rate_24h']?.state ?? '0';
return `${v}%`;
]]]
entity: sensor.joanna_all_dispatches_30d
name: All Dispatches 30D
icon: mdi:calendar-month
- type: custom:button-card
template: bearstone_infra_list_row
template: bearstone_infra_status_kpi
entity: binary_sensor.joanna_qmd_healthy
name: QMD Health
icon: mdi:heart-pulse
state_display: >
[[[
const qmd = states['sensor.bearclaw_status_telemetry']?.attributes?.qmdHealth || {};
const healthy = states['binary_sensor.joanna_qmd_healthy']?.state === 'on';
const status = qmd?.status || (healthy ? 'healthy' : 'unknown');
const failures = states['sensor.joanna_qmd_consecutive_failures']?.state ?? '0';
return `${status} | fail ${failures}`;
]]]
variables:
ok_state: 'on'
healthy_text: HEALTHY
alert_text: DEGRADED
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.joanna_memory_docs
@ -162,21 +181,10 @@
icon: mdi:database-search
state_display: >
[[[
const docs = states['sensor.joanna_memory_docs']?.state ?? '0';
const docs = entity?.state ?? '0';
const chunks = states['sensor.joanna_memory_chunks']?.state ?? '0';
const stale = states['binary_sensor.joanna_memory_index_stale']?.state === 'on';
return `${docs} docs | ${chunks} chunks${stale ? ' | stale' : ''}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.joanna_session_source_indexed
name: Session Transcript Indexing
icon: mdi:message-text-clock-outline
state_display: >
[[[
const enabled = states['binary_sensor.joanna_session_source_indexed']?.state === 'on';
const age = states['sensor.joanna_minutes_since_memory_index']?.state ?? '0';
return `${enabled ? 'enabled' : 'disabled'} | ${age} min ago`;
return `${docs} docs | ${chunks} chunks${stale ? ' | STALE' : ''}`;
]]]
- type: custom:vertical-stack-in-card
@ -186,6 +194,7 @@
- type: custom:button-card
template: bearstone_infra_panel_header
name: Codex Reset Credits
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.codex_reset_credit_status
@ -194,125 +203,90 @@
state_display: >
[[[
const usage = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage || {};
const status = usage.status || 'unknown';
const ok = usage.ok === true;
const last = usage.lastCheckAt ? String(usage.lastCheckAt).replace('T', ' ').slice(0, 16) : 'not checked';
return `${ok ? 'ok' : status} | ${last}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.codex_reset_credit_next_expiration
name: Next Expiration
icon: mdi:calendar-clock
state_display: >
[[[
const usage = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage || {};
const days = usage.minDaysRemaining;
const expRaw = usage.soonestExpiresAt || '';
const exp = expRaw ? String(expRaw).replace('T', ' ').slice(0, 16) : 'n/a';
const dayText = Number.isFinite(Number(days)) ? `${Number(days).toFixed(1)}d` : 'n/a';
return `${dayText} | ${exp}`;
return `${usage.ok === true ? 'ok' : (usage.status || 'unknown')} | ${last}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
template: bearstone_infra_status_kpi
entity: binary_sensor.codex_reset_credit_expiring_soon
name: Repair Window
icon: mdi:alert-clock
name: Next Expiration
icon: mdi:calendar-alert
variables:
ok_state: 'off'
state_display: >
[[[
const usage = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage || {};
const warn = usage.warnDays ?? 7;
const count = usage.warningCount ?? 0;
return `${count} within ${warn}d`;
const raw = states['sensor.codex_reset_credit_next_expiration']?.state;
const days = Number(states['sensor.codex_reset_credit_min_days_remaining']?.state);
if (!raw || raw === 'unknown' || raw === 'unavailable') return 'NO DATA';
const when = new Date(raw).toLocaleString('en-US', {
month: 'short', day: 'numeric', year: 'numeric', hour: 'numeric', minute: '2-digit'
});
return `${when} | ${Number.isFinite(days) ? days.toFixed(1) + ' DAYS' : ''}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.bearclaw_status_telemetry
name: Reset #1
icon: mdi:backup-restore
entity: sensor.codex_reset_credit_status
name: Reset Slot 1
icon: mdi:numeric-1-box-outline
state_display: >
[[[
const credits = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage?.credits || [];
const credit = credits[0];
if (!credit) return 'No reset';
const days = Number.isFinite(Number(credit.days_remaining)) ? `${Number(credit.days_remaining).toFixed(1)}d` : 'n/a';
const exp = credit.expires_at ? String(credit.expires_at).replace('T', ' ').slice(0, 16) : 'n/a';
return `${credit.status || 'unknown'} | ${days} | ${exp}`;
const credit = (entity?.attributes?.credits || []).find(item => Number(item.index) === 1);
if (!credit?.expires_at) return 'NO DATA';
const when = new Date(credit.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
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.bearclaw_status_telemetry
name: Reset #2
icon: mdi:backup-restore
entity: sensor.codex_reset_credit_status
name: Reset Slot 2
icon: mdi:numeric-2-box-outline
state_display: >
[[[
const credits = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage?.credits || [];
const credit = credits[1];
if (!credit) return 'No reset';
const days = Number.isFinite(Number(credit.days_remaining)) ? `${Number(credit.days_remaining).toFixed(1)}d` : 'n/a';
const exp = credit.expires_at ? String(credit.expires_at).replace('T', ' ').slice(0, 16) : 'n/a';
return `${credit.status || 'unknown'} | ${days} | ${exp}`;
const credit = (entity?.attributes?.credits || []).find(item => Number(item.index) === 2);
if (!credit?.expires_at) return 'NO DATA';
const when = new Date(credit.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
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.bearclaw_status_telemetry
name: Reset #3
icon: mdi:backup-restore
entity: sensor.codex_reset_credit_status
name: Reset Slot 3
icon: mdi:numeric-3-box-outline
state_display: >
[[[
const credits = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage?.credits || [];
const credit = credits[2];
if (!credit) return 'No reset';
const days = Number.isFinite(Number(credit.days_remaining)) ? `${Number(credit.days_remaining).toFixed(1)}d` : 'n/a';
const exp = credit.expires_at ? String(credit.expires_at).replace('T', ' ').slice(0, 16) : 'n/a';
return `${credit.status || 'unknown'} | ${days} | ${exp}`;
const credit = (entity?.attributes?.credits || []).find(item => Number(item.index) === 3);
if (!credit?.expires_at) return 'NO DATA';
const when = new Date(credit.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
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.bearclaw_status_telemetry
name: Reset #4
icon: mdi:backup-restore
entity: sensor.codex_reset_credit_status
name: Reset Slot 4
icon: mdi:numeric-4-box-outline
state_display: >
[[[
const credits = states['sensor.bearclaw_status_telemetry']?.attributes?.codexUsage?.credits || [];
const credit = credits[3];
if (!credit) return 'No reset';
const days = Number.isFinite(Number(credit.days_remaining)) ? `${Number(credit.days_remaining).toFixed(1)}d` : 'n/a';
const exp = credit.expires_at ? String(credit.expires_at).replace('T', ' ').slice(0, 16) : 'n/a';
return `${credit.status || 'unknown'} | ${days} | ${exp}`;
const credit = (entity?.attributes?.credits || []).find(item => Number(item.index) === 4);
if (!credit?.expires_at) return 'NO DATA';
const when = new Date(credit.expires_at).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' });
const days = Number(credit.days_remaining);
return `${when} | ${Number.isFinite(days) ? days.toFixed(1) + ' DAYS' : String(credit.status || '').toUpperCase()}`;
]]]
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:layout-card
grid_options:
columns: full
layout_type: custom:grid-layout
layout:
grid-template-columns: repeat(1, minmax(0, 1fr))
grid-auto-flow: row
grid-auto-rows: min-content
grid-gap: 12px
margin: 0
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Joanna Activity
# Fallback card: Lovelace logbook card is required to show filtered activity history.
- type: logbook
hours_to_show: 72
entities:
- automation.bearclaw_reply_webhook
- automation.codex_reset_credit_expiry_repair
- automation.tugtainer_dispatch_joanna_for_available_updates
- binary_sensor.codex_reset_credit_expiring_soon
- script.joanna_dispatch
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

@ -3,64 +3,182 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - mariadb sections
# Reusable list extracted from a view for smaller diffs and safer edits.
# Infrastructure Partial - MariaDB
# Humanized KPIs, seven-day capacity trends, recorder health, and tuning.
# -------------------------------------------------------------------
# Notes: Stitch-inspired MariaDB layout (KPI strip + 3 panel columns).
# Notes: Database growth, ingest rate, and connections use consistent 168-hour graphs.
######################################################################
# -------------------------------------------------------------------
# Humanized KPI strip (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 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.database_size
name: DB Size
name: Database Size
icon: mdi:database
state_display: >
[[[
const mib = Number(entity?.state);
return Number.isFinite(mib) ? `${(mib / 1024).toFixed(1)} GiB` : 'NO DATA';
]]]
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.database_tables_count
name: Tables
icon: mdi:table
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_connections
name: Conn.
name: Connections
icon: mdi:lan-connect
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_uptime
name: Uptime
icon: mdi:timer-outline
state_display: >
[[[
const seconds = Number(entity?.state);
if (!Number.isFinite(seconds)) return 'NO DATA';
const days = Math.floor(seconds / 86400);
const hours = Math.floor((seconds % 86400) / 3600);
return `${days}d ${hours}h`;
]]]
# -------------------------------------------------------------------
# Capacity and workload trends
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
square: false
cards:
- type: custom:mini-graph-card
name: Database Growth
icon: mdi:database-arrow-up
hours_to_show: 168
points_per_hour: 0.25
line_width: 2
smoothing: true
show:
legend: false
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.database_size
name: Database Size
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Recorder Ingest Rate
icon: mdi:chart-line
hours_to_show: 168
points_per_hour: 0.25
line_width: 2
smoothing: true
show:
legend: false
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.database_records_per_day
name: Records per Day
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Active Connections
icon: mdi:lan-connect
hours_to_show: 168
points_per_hour: 0.25
line_width: 2
smoothing: true
show:
legend: false
labels: false
name: true
icon: true
state: true
entities:
- entity: sensor.mariadb_connections
name: Connections
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
# -------------------------------------------------------------------
# Recorder and server health
# -------------------------------------------------------------------
- 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: dense
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: Database Statistics
name: Recorder Health
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.database_total_records
name: Total Records
icon: mdi:counter
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.database_records_per_day
name: Records per Day
icon: mdi:chart-line
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.database_oldest_record
name: Oldest Record
name: Oldest Retained Record
icon: mdi:clock-start
- type: custom:vertical-stack-in-card
@ -70,82 +188,104 @@
- type: custom:button-card
template: bearstone_infra_panel_header
name: MariaDB Status
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: sensor.mariadb_status
name: Server Status
icon: mdi:database
state_display: >
[[[
if (entity.state === 'unknown') return 'Unknown';
return String(entity.state).toUpperCase();
]]]
icon: mdi:database-check
state_display: '[[[ return String(entity.state ?? "NO DATA").toUpperCase(); ]]]'
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.mariadb_version
name: Version
icon: mdi:information-outline
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.mariadb_performance
name: Performance
icon: mdi:chart-line
name: Query Rate
icon: mdi:speedometer
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.mariadb_questions
name: Total Queries
icon: mdi:database-search
- type: custom:vertical-stack-in-card
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
# -------------------------------------------------------------------
# Static tuning reference
# -------------------------------------------------------------------
- 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: dense
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:button-card
template: bearstone_infra_panel_header
name: MariaDB Tuning
- type: grid
columns: 2
square: false
cards:
name: MariaDB Tuning Reference
view_layout:
grid-column: 1 / -1
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_buffer_pool_size
name: InnoDB Buffer
icon: mdi:memory
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_max_connections
name: Max Conn.
name: Max Connections
icon: mdi:account-multiple
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_log_file_size
name: Log File
icon: mdi:file
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_tmp_table_size
name: Temp Size
name: Temp Table Size
icon: mdi:table
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_io_capacity
name: IO Capacity
icon: mdi:speedometer
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_io_threads
name: IO Threads
icon: mdi:developer-board
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_table_cache
name: Table Cache
icon: mdi:cached
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.mariadb_buffer_sizes
name: Sort/Join
name: Sort / Join
icon: mdi:code-brackets

@ -0,0 +1,315 @@
######################################################################
# @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 - Network, DNS, and external services
# Current WAN telemetry, dual Pi-hole operations, and website health.
# -------------------------------------------------------------------
# Notes: Pi-hole blocking controls are mirrored by pihole_ha.yaml.
# Notes: Detailed Pi-hole and website telemetry live in hidden drill-down views.
######################################################################
# -------------------------------------------------------------------
# Current network health
# -------------------------------------------------------------------
- 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(6, 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_status_kpi
entity: binary_sensor.infra_wan_quality_degraded
name: WAN
icon: mdi:wan
variables:
ok_state: 'off'
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.speedtest_download
name: Download
icon: mdi:download-network-outline
state_display: >
[[[
const value = Number(entity?.state);
const unit = entity?.attributes?.unit_of_measurement ?? 'Mbit/s';
return Number.isFinite(value) ? `${value.toFixed(0)} ${unit}` : 'NO DATA';
]]]
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.speedtest_upload
name: Upload
icon: mdi:upload-network-outline
state_display: >
[[[
const value = Number(entity?.state);
const unit = entity?.attributes?.unit_of_measurement ?? 'Mbit/s';
return Number.isFinite(value) ? `${value.toFixed(0)} ${unit}` : 'NO DATA';
]]]
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.speedtest_ping
name: Latency
icon: mdi:timer-outline
state_display: >
[[[
const value = Number(entity?.state);
const unit = entity?.attributes?.unit_of_measurement ?? 'ms';
return Number.isFinite(value) ? `${value.toFixed(1)} ${unit}` : 'NO DATA';
]]]
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.pi_hole_status
name: Primary DNS
icon: mdi:pi-hole
variables:
ok_state: 'on'
healthy_text: RUNNING
alert_text: DOWN
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.pi_hole_status_2
name: Secondary DNS
icon: mdi:pi-hole
variables:
ok_state: 'on'
healthy_text: RUNNING
alert_text: DOWN
# -------------------------------------------------------------------
# Trends, Pi-hole controls, and website health
# -------------------------------------------------------------------
- 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(3, minmax(0, 1fr))
grid-auto-flow: row
grid-auto-rows: min-content
grid-gap: 12px
margin: 0
mediaquery:
"(max-width: 1200px)":
grid-template-columns: repeat(2, minmax(0, 1fr))
"(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: Internet
- type: custom:mini-graph-card
name: Internet Throughput
icon: mdi:speedometer
hours_to_show: 24
points_per_hour: 1
line_width: 2
smoothing: true
show:
fill: false
legend: true
labels: false
name: true
icon: true
state: false
entities:
- entity: sensor.speedtest_download
name: Download
show_legend_state: true
- entity: sensor.speedtest_upload
name: Upload
show_legend_state: true
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.speedtest_ping
name: Current Latency
icon: mdi:wan
state_display: >
[[[
const value = Number(entity?.state);
const unit = entity?.attributes?.unit_of_measurement ?? 'ms';
return Number.isFinite(value) ? `${value.toFixed(1)} ${unit}` : 'NO DATA';
]]]
- 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: Pi-hole DNS
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.pi_hole_status
name: Primary Service
icon: mdi:dns
state_display: >
[[[ return entity?.state === 'on' ? 'RUNNING' : 'DOWN'; ]]]
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: switch.pi_hole
name: Primary Blocking
icon: mdi:shield-check-outline
state_display: >
[[[ return entity?.state === 'on' ? 'ENABLED' : 'DISABLED'; ]]]
tap_action:
action: toggle
confirmation:
text: Toggle Pi-hole blocking? Home Assistant mirrors this setting to both DNS nodes.
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.pi_hole_status_2
name: Secondary Service
icon: mdi:dns
state_display: >
[[[ return entity?.state === 'on' ? 'RUNNING' : 'DOWN'; ]]]
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: switch.pi_hole_2
name: Secondary Blocking
icon: mdi:shield-check-outline
state_display: >
[[[ return entity?.state === 'on' ? 'ENABLED' : 'DISABLED'; ]]]
tap_action:
action: toggle
confirmation:
text: Toggle Pi-hole blocking? Home Assistant mirrors this setting to both DNS nodes.
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.infra_nebula_sync_dns_consistency
name: Primary / Secondary Sync
icon: mdi:database-sync-outline
state_display: >
[[[ return String(entity?.state ?? 'NO DATA').toUpperCase(); ]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.infra_dns_pihole_degraded
name: Open Detailed Pi-hole Analytics
icon: mdi:chart-box-outline
state_display: >
[[[ return 'OPEN'; ]]]
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/pihole
- 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: Website Health
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.vcloudinfo_com
name: vcloudinfo.com
icon: mdi:web
state_display: >
[[[ return entity?.state === 'on' ? 'UP' : String(entity?.state ?? 'NO DATA').toUpperCase(); ]]]
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.www_kingcrafthomes_com
name: kingcrafthomes.com
icon: mdi:web
state_display: >
[[[ return entity?.state === 'on' ? 'UP' : String(entity?.state ?? 'NO DATA').toUpperCase(); ]]]
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.bear_stone
name: Bear Stone
icon: mdi:web-check
state_display: >
[[[ return entity?.state === 'on' ? 'UP' : String(entity?.state ?? 'NO DATA').toUpperCase(); ]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.infra_website_down_count
name: Monitoring Coverage
icon: mdi:cloud-check-outline
state_display: >
[[[
const down = Number(entity?.state);
if (!Number.isFinite(down)) return 'NO DATA';
return `${3 - down}/3 MONITORS UP`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.ipmer_com_days_until_expiration
name: Next Domain Expiry
icon: mdi:calendar-clock
state_display: >
[[[
const domains = [
['vcloudinfo.com', 'sensor.vcloudinfo_com_days_until_expiration', 'sensor.vcloudinfo_com_expires'],
['ipmer.com', 'sensor.ipmer_com_days_until_expiration', 'sensor.ipmer_com_expires'],
['kingcraftshomes.com', 'sensor.kingcrafthomes_com_days_until_expiration', 'sensor.kingcrafthomes_com_expires'],
];
const candidates = domains
.map(([name, daysId, expiresId]) => ({
name,
days: Number(states[daysId]?.state),
expires: String(states[expiresId]?.state ?? ''),
}))
.filter((item) => Number.isFinite(item.days))
.sort((a, b) => a.days - b.days);
if (!candidates.length) return 'NO DATA';
const next = candidates[0];
const date = /^\d{4}-\d{2}-\d{2}/.test(next.expires)
? next.expires.slice(0, 10)
: 'DATE UNKNOWN';
return `${next.name.toUpperCase()} | ${Math.round(next.days)} DAYS | ${date}`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.infra_website_degraded
name: Open Website Details
icon: mdi:chart-box-outline
state_display: >
[[[ return 'OPEN'; ]]]
tap_action:
action: navigate
navigation_path: /dashboard-infrastructure/website-health

@ -0,0 +1,50 @@
######################################################################
# @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 - Pi-hole detail drill-down
# Large analytics cards for both primary and secondary DNS nodes.
# -------------------------------------------------------------------
# Notes: Kept out of the primary Network view to preserve dense operations layout.
######################################################################
- 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: 1200px)":
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: Primary DNS
- type: custom:pi-hole
device_id: d69637da16f7d7f3626070582be59808
- 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: Secondary DNS
- type: custom:pi-hole
device_id: e6ca0f9504eaa7908cc84b5d77571d61

@ -1,26 +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 - Pi-hole sections
# Dedicated full-width Pi-hole panel.
# -------------------------------------------------------------------
# Notes: Uses `custom:pi-hole` with existing device_id.
######################################################################
- type: grid
column_span: 4
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:pi-hole
device_id: d69637da16f7d7f3626070582be59808
grid_options:
columns: full

@ -3,74 +3,97 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - proxmox sections
# Reusable list extracted from a view for smaller diffs and safer edits.
# Infrastructure Partial - Proxmox
# Runtime/update status with one-metric host, VM, and temperature trends.
# -------------------------------------------------------------------
# Related Issue: 1584
# Notes: Standardized on `custom:button-card` + `custom:mini-graph-card` with `card_mod` polish.
# Notes: All graphs use 24-hour windows and explicit threshold colors.
# Notes: Pending updates remain windowed maintenance signals, not runtime failures.
######################################################################
# -------------------------------------------------------------------
# Host status strip (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 6
columns: 1
square: false
cards:
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.node_proxmox1_cpu_used
name: P1 CPU
icon: mdi:cpu-64-bit
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.node_proxmox1_memory_used_percentage
name: P1 MEM
icon: mdi:memory
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.proxmox1_disk_used_percentage
name: P1 DISK
icon: mdi:harddisk
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.node_proxmox02_cpu_used
name: P2 CPU
icon: mdi:cpu-64-bit
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.node_proxmox02_memory_used_percentage
name: P2 MEM
icon: mdi:memory
- type: custom:button-card
template: bearstone_infra_kpi
entity: sensor.proxmox02_disk_used_percentage
name: P2 DISK
icon: mdi:harddisk
- type: grid
column_span: 4
columns: 2
square: false
- 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_list_row_running
template: bearstone_infra_status_kpi
entity: binary_sensor.proxmox1_runtime_healthy
name: Proxmox01 Runtime
icon: mdi:server
state_display: >
[[[ return entity.state === 'on' ? 'ONLINE' : 'OFFLINE'; ]]]
variables:
ok_state: 'on'
healthy_text: ONLINE
alert_text: OFFLINE
- type: custom:button-card
template: bearstone_infra_list_row_running
template: bearstone_infra_status_kpi
entity: binary_sensor.proxmox02_runtime_healthy
name: Proxmox02 Runtime
icon: mdi:server
state_display: >
[[[ return entity.state === 'on' ? 'ONLINE' : 'OFFLINE'; ]]]
variables:
ok_state: 'on'
healthy_text: ONLINE
alert_text: OFFLINE
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.node_proxmox1_updates_packages
name: Proxmox01 Updates
icon: mdi:package-up
variables:
ok_state: 'off'
healthy_text: CURRENT
alert_text: PENDING
- type: custom:button-card
template: bearstone_infra_status_kpi
entity: binary_sensor.node_proxmox02_updates_packages
name: Proxmox02 Updates
icon: mdi:package-up
variables:
ok_state: 'off'
healthy_text: CURRENT
alert_text: PENDING
# -------------------------------------------------------------------
# Host resource trends
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
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: dense
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
@ -78,48 +101,84 @@
- type: custom:button-card
template: bearstone_infra_panel_header
name: Proxmox01
- type: custom:mini-graph-card
name: Host CPU / Memory / Disk
icon: mdi:server
name: CPU Utilization
icon: mdi:cpu-64-bit
hours_to_show: 24
points_per_hour: 2
line_width: 2
animate: true
smoothing: true
show:
fill: true
legend: false
icon: true
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 60
color: '#f39c12'
- value: 85
color: '#c0392b'
entities:
- entity: sensor.node_proxmox1_cpu_used
name: CPU
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Memory Utilization
icon: mdi:memory
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
legend: false
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 70
color: '#f39c12'
- value: 90
color: '#c0392b'
entities:
- entity: sensor.node_proxmox1_memory_used_percentage
name: Memory
- entity: sensor.proxmox1_disk_used_percentage
name: Disk
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: VMs CPU / Memory
icon: mdi:chart-line
name: Disk Utilization
icon: mdi:harddisk
hours_to_show: 24
points_per_hour: 2
line_width: 2
animate: true
smoothing: true
show:
fill: false
legend: false
icon: true
labels: false
name: true
state: false
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 70
color: '#f39c12'
- value: 85
color: '#c0392b'
entities:
- entity: sensor.qemu_docker69_169_cpu_used
name: Docker69 CPU
- entity: sensor.qemu_docker69_169_memory_used_percentage
name: Docker69 MEM
- entity: sensor.qemu_carlo_hass_105_cpu_used
name: HASS CPU
- entity: sensor.qemu_carlo_hass_105_memory_used_percentage
name: HASS MEM
- entity: sensor.proxmox1_disk_used_percentage
name: Disk
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:vertical-stack-in-card
card_mod:
@ -128,66 +187,163 @@
- type: custom:button-card
template: bearstone_infra_panel_header
name: Proxmox02
- type: custom:mini-graph-card
name: Host CPU / Memory / Disk
icon: mdi:server
name: CPU Utilization
icon: mdi:cpu-64-bit
hours_to_show: 24
points_per_hour: 2
line_width: 2
animate: true
smoothing: true
show:
fill: true
legend: false
icon: true
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 60
color: '#f39c12'
- value: 85
color: '#c0392b'
entities:
- entity: sensor.node_proxmox02_cpu_used
name: CPU
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Memory Utilization
icon: mdi:memory
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
legend: false
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 70
color: '#f39c12'
- value: 90
color: '#c0392b'
entities:
- entity: sensor.node_proxmox02_memory_used_percentage
name: Memory
- entity: sensor.proxmox02_disk_used_percentage
name: Disk
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Frigate VM CPU / Memory
icon: mdi:video
name: Disk Utilization
icon: mdi:harddisk
hours_to_show: 24
points_per_hour: 2
line_width: 2
animate: true
smoothing: true
show:
fill: false
legend: false
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 0
color: '#43a047'
- value: 70
color: '#f39c12'
- value: 85
color: '#c0392b'
entities:
- entity: sensor.proxmox02_disk_used_percentage
name: Disk
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
# -------------------------------------------------------------------
# VM and thermal trends (same-unit series only)
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
square: false
cards:
- type: custom:mini-graph-card
name: VM CPU Utilization
icon: mdi:chart-line
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
legend: true
labels: false
name: true
icon: true
state: false
entities:
- entity: sensor.qemu_docker69_169_cpu_used
name: Docker69
- entity: sensor.qemu_carlo_hass_105_cpu_used
name: Home Assistant
- entity: sensor.qemu_docker2_101_cpu_used
name: CPU
- entity: sensor.qemu_docker2_101_memory_used_percentage
name: MEM
name: Frigate
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:vertical-stack-in-card
- type: custom:mini-graph-card
name: VM Memory Utilization
icon: mdi:memory
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
legend: true
labels: false
name: true
icon: true
state: false
entities:
- entity: sensor.qemu_docker69_169_memory_used_percentage
name: Docker69
- entity: sensor.qemu_carlo_hass_105_memory_used_percentage
name: Home Assistant
- entity: sensor.qemu_docker2_101_memory_used_percentage
name: Frigate
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Garage Temp
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml
- type: custom:mini-graph-card
name: Garage Temperature
icon: mdi:thermometer
hours_to_show: 24
points_per_hour: 2
line_width: 2
smoothing: true
show:
legend: true
labels: false
name: true
icon: true
state: true
color_thresholds:
- value: 80
color: '#43a047'
- value: 135
color: '#f39c12'
- value: 145
color: '#c0392b'
entities:
- entity: sensor.proxmox_garage_average_temperature
name: Garage
- entity: sensor.pirateweather_temperature
name: Outside
name: Garage Temp
hours_to_show: 96
color_thresholds:
- value: 50
color: '#f39c12'
- value: 120
color: '#d35400'
- value: 145
color: '#c0392b'
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_card.yaml

@ -3,15 +3,14 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure Partial - Website health sections
# Website uptime + response windows + domain expiry telemetry view.
# Infrastructure Partial - Website health
# Current UptimeRobot availability, domain registration, and actionable exceptions.
# -------------------------------------------------------------------
# Related Issue: 1584
# Notes: Uses direct Uptime Kuma entities (no aggregate uptime min sensors).
# Notes: The installed UptimeRobot integration exposes current monitor state, not Kuma-style rollups.
######################################################################
# -------------------------------------------------------------------
# Website hero (mandatory first full-width container)
# Current health and domain registration (mandatory first full-width wrapper)
# -------------------------------------------------------------------
- type: grid
column_span: 4
@ -33,233 +32,116 @@
grid-template-columns: repeat(1, minmax(0, 1fr))
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Website Uptime
- type: custom:layout-card
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:
name: Current Website Health
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.vcloudinfo_com
name: vcloudinfo.com
icon: mdi:web
state_display: >
[[[ return entity.state === 'on' ? 'UP' : (entity.state || 'unknown').toUpperCase(); ]]]
state_display: '[[[ return entity.state === "on" ? "UP" : String(entity.state || "unknown").toUpperCase(); ]]]'
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.www_kingcrafthomes_com
name: kingcrafthomes.com
icon: mdi:web
state_display: >
[[[ return entity.state === 'on' ? 'UP' : (entity.state || 'unknown').toUpperCase(); ]]]
state_display: '[[[ return entity.state === "on" ? "UP" : String(entity.state || "unknown").toUpperCase(); ]]]'
- type: custom:button-card
template: bearstone_infra_list_row_running
entity: binary_sensor.bear_stone
name: Bear Stone
icon: mdi:web-check
state_display: >
[[[ return entity.state === 'on' ? 'UP' : (entity.state || 'unknown').toUpperCase(); ]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.vcloudinfo_com
name: vcloudinfo.com uptime
icon: mdi:chart-line
state_display: >
[[[
const pick = (metric, window) => {
const suffix = `${metric}_${window}`;
const candidates = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
k.includes('vcloudinfo_com') &&
k.endsWith(suffix)
);
const eid = candidates[0];
return eid ? (states[eid]?.state ?? 'n/a') : 'n/a';
};
const uptime1d = pick('uptime', '1d');
const uptime30d = pick('uptime', '30d');
const uptime365d = pick('uptime', '365d');
return `1d ${uptime1d}% | 30d ${uptime30d}% | 365d ${uptime365d}%`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.vcloudinfo_com
name: vcloudinfo.com response
icon: mdi:timer-outline
state_display: >
[[[
const pick = (metric, window) => {
const suffix = `${metric}_${window}`;
const candidates = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
k.includes('vcloudinfo_com') &&
k.endsWith(suffix)
);
const eid = candidates[0];
return eid ? (states[eid]?.state ?? 'n/a') : 'n/a';
};
const r1d = pick('avg_response_time', '1d');
const r30d = pick('avg_response_time', '30d');
const r365d = pick('avg_response_time', '365d');
return `1d ${r1d}s | 30d ${r30d}s | 365d ${r365d}s`;
]]]
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.www_kingcrafthomes_com
name: kingcrafthomes.com uptime
icon: mdi:chart-line
state_display: >
[[[
const pick = (metric, window) => {
const suffix = `${metric}_${window}`;
const candidates = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
(k.includes('kingcrafthomes_com') || k.includes('www_kingcrafthomes_com')) &&
k.endsWith(suffix)
);
const eid = candidates[0];
return eid ? (states[eid]?.state ?? 'n/a') : 'n/a';
};
const uptime1d = pick('uptime', '1d');
const uptime30d = pick('uptime', '30d');
const uptime365d = pick('uptime', '365d');
return `1d ${uptime1d}% | 30d ${uptime30d}% | 365d ${uptime365d}%`;
]]]
state_display: '[[[ return entity.state === "on" ? "UP" : String(entity.state || "unknown").toUpperCase(); ]]]'
- type: custom:button-card
template: bearstone_infra_list_row
entity: binary_sensor.www_kingcrafthomes_com
name: kingcrafthomes.com response
icon: mdi:timer-outline
entity: sensor.infra_website_down_count
name: Monitoring Coverage
icon: mdi:cloud-check-outline
state_display: >
[[[
const pick = (metric, window) => {
const suffix = `${metric}_${window}`;
const candidates = Object.keys(states).filter((k) =>
k.startsWith('sensor.') &&
(k.includes('kingcrafthomes_com') || k.includes('www_kingcrafthomes_com')) &&
k.endsWith(suffix)
);
const eid = candidates[0];
return eid ? (states[eid]?.state ?? 'n/a') : 'n/a';
};
const r1d = pick('avg_response_time', '1d');
const r30d = pick('avg_response_time', '30d');
const r365d = pick('avg_response_time', '365d');
return `1d ${r1d}s | 30d ${r30d}s | 365d ${r365d}s`;
const down = Number(entity?.state);
if (!Number.isFinite(down)) return 'NO DATA';
return `${3 - down}/3 CURRENT MONITORS UP`;
]]]
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Domain Expiry
- type: custom:layout-card
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:
name: Domain Registration Expiration
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.vcloudinfo_com_days_until_expiration
name: vcloudinfo.com
icon: mdi:calendar-clock
state_display: >
[[[
const days = states['sensor.vcloudinfo_com_days_until_expiration']?.state ?? 'n/a';
const expRaw = states['sensor.vcloudinfo_com_expires']?.state;
let exp = 'n/a';
const s = String(expRaw ?? '');
if (s && !['unknown','unavailable','none',''].includes(s.toLowerCase())) {
if (/^\d{4}-\d{2}-\d{2}/.test(s)) {
exp = s.slice(0, 10);
} else {
const dt = new Date(s);
if (!Number.isNaN(dt.getTime())) {
const y = dt.getFullYear();
const m = String(dt.getMonth() + 1).padStart(2, '0');
const d = String(dt.getDate()).padStart(2, '0');
exp = `${y}-${m}-${d}`;
}
}
}
return `${days}d | ${exp}`;
]]]
state_display: '[[[ return `${entity.state} days`; ]]]'
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.ipmer_com_days_until_expiration
name: ipmer.com
icon: mdi:calendar-clock
state_display: '[[[ return `${entity.state} days`; ]]]'
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.kingcrafthomes_com_days_until_expiration
name: kingcrafthomes.com
icon: mdi:calendar-clock
state_display: >
[[[
const days = states['sensor.kingcrafthomes_com_days_until_expiration']?.state ?? 'n/a';
const expRaw = states['sensor.kingcrafthomes_com_expires']?.state;
let exp = 'n/a';
const s = String(expRaw ?? '');
if (s && !['unknown','unavailable','none',''].includes(s.toLowerCase())) {
if (/^\d{4}-\d{2}-\d{2}/.test(s)) {
exp = s.slice(0, 10);
} else {
const dt = new Date(s);
if (!Number.isNaN(dt.getTime())) {
const y = dt.getFullYear();
const m = String(dt.getMonth() + 1).padStart(2, '0');
const d = String(dt.getDate()).padStart(2, '0');
exp = `${y}-${m}-${d}`;
}
}
}
return `${days}d | ${exp}`;
]]]
state_display: '[[[ return `${entity.state} days`; ]]]'
# -------------------------------------------------------------------
# Actionable exceptions
# -------------------------------------------------------------------
- type: grid
column_span: 4
columns: 3
columns: 1
square: false
cards:
- type: custom:vertical-stack-in-card
grid_options:
columns: full
card_mod:
style: !include /config/dashboards/infrastructure/card_mod/infra_panel.yaml
cards:
- type: custom:button-card
template: bearstone_infra_panel_header
name: Website Exceptions
- type: custom:button-card
template: bearstone_infra_list_row
entity: sensor.infra_website_down_count
name: Current Availability Summary
icon: mdi:web-check
state_display: >
[[[
const count = Number(entity?.state);
if (!Number.isFinite(count)) return 'NO DATA';
return count === 0 ? 'ALL MONITORS UP' : `${count} MONITORS DOWN`;
]]]
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.infra_website_degraded
name: Website status degraded
name: Website availability degraded
icon: mdi:web-off
variables:
alert_kind: binary_on
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.infra_website_uptime_slo_breach
name: Uptime SLO breach
icon: mdi:alert-decagram
variables:
alert_kind: binary_on
- type: custom:button-card
template: bearstone_infra_alert_row
entity: binary_sensor.infra_website_latency_degraded
name: Website latency degraded
icon: mdi:timer-alert
variables:
alert_kind: binary_on
template: bearstone_infra_list_row
entity: sensor.infra_website_down_count
name: Telemetry Scope
icon: mdi:information-outline
state_display: '[[[ return "CURRENT UP/DOWN | UPTIMEROBOT"; ]]]'

@ -731,6 +731,8 @@ bearstone_infra_docker_volume_row:
return `${value.toFixed(precision)} MiB`;
};
if (variables?.not_instrumented === true) return 'Not instrumented';
const totalEntity = variables && variables.total_entity
? states[variables.total_entity]
: entity;
@ -784,7 +786,7 @@ bearstone_infra_alert_row:
if (kind === 'binary_on') show = (stateStr === 'on');
else if (kind === 'binary_off') show = (stateStr === 'off');
else if (kind === 'ap_zero') show = (stateStr === '0' && ageS >= minAge);
else if (kind === 'disk_high') {
else if (kind === 'disk_high' || kind === 'numeric_high') {
const v = parseFloat(stateStr);
show = Number.isFinite(v) && (thr !== null ? (v > thr) : (v > 80));
} else if (kind === 'speedtest_slow') {
@ -889,3 +891,152 @@ bearstone_infra_kpi:
- font-size: 16px
- font-weight: 800
- opacity: 0.9
bearstone_infra_status_kpi:
template: bearstone_infra_kpi
state_display: >
[[[
const raw = String(entity?.state ?? '').toLowerCase();
const unavailable = ['unknown', 'unavailable', 'none', ''].includes(raw);
const okState = String(variables?.ok_state ?? 'off').toLowerCase();
if (unavailable) return 'NO DATA';
if (raw === okState) return variables?.healthy_text ?? 'HEALTHY';
if (variables?.alert_text) return variables.alert_text;
if (variables?.raw_suffix) return `${entity.state} ${variables.raw_suffix}`;
return 'ATTENTION';
]]]
styles:
card:
- border-color: >
[[[
const raw = String(entity?.state ?? '').toLowerCase();
const okState = String(variables?.ok_state ?? 'off').toLowerCase();
if (['unknown', 'unavailable', 'none', ''].includes(raw)) return 'rgba(245,124,0,0.45)';
return raw === okState ? 'rgba(67,160,71,0.45)' : 'rgba(229,57,53,0.40)';
]]]
- background: >
[[[
const raw = String(entity?.state ?? '').toLowerCase();
const okState = String(variables?.ok_state ?? 'off').toLowerCase();
if (['unknown', 'unavailable', 'none', ''].includes(raw)) return 'rgba(255,243,224,0.85)';
return raw === okState ? 'rgba(232,245,233,0.85)' : 'rgba(255,235,238,0.85)';
]]]
icon:
- color: >
[[[
const raw = String(entity?.state ?? '').toLowerCase();
const okState = String(variables?.ok_state ?? 'off').toLowerCase();
if (['unknown', 'unavailable', 'none', ''].includes(raw)) return 'rgba(245,124,0,1)';
return raw === okState ? 'rgba(46,125,50,1)' : 'rgba(198,40,40,1)';
]]]
state:
- color: >
[[[
const raw = String(entity?.state ?? '').toLowerCase();
const okState = String(variables?.ok_state ?? 'off').toLowerCase();
if (['unknown', 'unavailable', 'none', ''].includes(raw)) return 'rgba(230,81,0,1)';
return raw === okState ? 'rgba(46,125,50,1)' : 'rgba(198,40,40,1)';
]]]
- opacity: 1
bearstone_infra_update_row:
template: bearstone_infra_list_row
icon: mdi:package-up
show_label: true
variables:
apt_severity: >
[[[
const state = String(entity?.state ?? '').toUpperCase();
if (!state || state.includes('UNKNOWN') || state.includes('UNAVAILABLE') || state.includes('ERROR')) return 'error';
const host = entity?.entity_id?.match(/^sensor\.(docker_\d+)_apt_status$/)?.[1];
const dueRaw = host ? states[`input_datetime.apt_${host}_action_due`]?.state : null;
const due = dueRaw && !['unknown', 'unavailable', 'none'].includes(String(dueRaw).toLowerCase())
? new Date(String(dueRaw).replace(' ', 'T'))
: null;
const actionable = state.includes('PENDING') || state.includes('REBOOT REQ');
if (actionable && due instanceof Date && !Number.isNaN(due.getTime()) && Date.now() > due.getTime()) return 'overdue';
if (actionable) return 'scheduled';
if (state.includes('NO UPDATES') || state.includes('UPDATED')) return 'healthy';
return 'neutral';
]]]
state_display: >
[[[ return String(entity?.state ?? 'NO DATA').toUpperCase(); ]]]
label: >
[[[
const state = String(entity?.state ?? '').toUpperCase();
const host = entity?.entity_id?.match(/^sensor\.(docker_\d+)_apt_status$/)?.[1];
const dueRaw = host ? states[`input_datetime.apt_${host}_action_due`]?.state : null;
const parseDate = (raw) => {
if (!raw || ['unknown', 'unavailable', 'none', ''].includes(String(raw).toLowerCase())) return null;
const value = new Date(String(raw).replace(' ', 'T'));
return Number.isNaN(value.getTime()) ? null : value;
};
const formatTime = (value) => value.toLocaleTimeString('en-US', {
hour: 'numeric',
minute: '2-digit'
}).toUpperCase();
const formatDue = (value) => {
const weekday = value.toLocaleDateString('en-US', { weekday: 'short' }).toUpperCase();
return `${weekday} ${formatTime(value)}`;
};
const actionable = state.includes('PENDING') || state.includes('REBOOT REQ');
const due = parseDate(dueRaw);
if (actionable) {
const action = state.includes('REBOOT REQ') ? 'REBOOT' : 'UPDATE';
return due ? `${action} ${formatDue(due)}` : `${action} SCHEDULED`;
}
return 'NO ACTION SCHEDULED';
]]]
styles:
grid:
- grid-template-areas: '"i n s" "i l l"'
- grid-template-columns: 24px 1fr auto
- grid-template-rows: min-content min-content
card:
- border-color: >
[[[
if (variables.apt_severity === 'overdue') return 'rgba(229,57,53,0.40)';
if (variables.apt_severity === 'error' || variables.apt_severity === 'scheduled') return 'rgba(245,124,0,0.45)';
if (variables.apt_severity === 'healthy') return 'rgba(67,160,71,0.45)';
return 'var(--divider-color)';
]]]
- background: >
[[[
if (variables.apt_severity === 'overdue') return 'rgba(255,235,238,0.85)';
if (variables.apt_severity === 'error' || variables.apt_severity === 'scheduled') return 'rgba(255,243,224,0.85)';
if (variables.apt_severity === 'healthy') return 'rgba(232,245,233,0.85)';
return 'rgba(0,0,0,0.02)';
]]]
icon:
- color: >
[[[
if (variables.apt_severity === 'overdue') return 'rgba(198,40,40,1)';
if (variables.apt_severity === 'error' || variables.apt_severity === 'scheduled') return 'rgba(230,81,0,1)';
if (variables.apt_severity === 'healthy') return 'rgba(46,125,50,1)';
return 'var(--primary-color)';
]]]
state:
- color: >
[[[
if (variables.apt_severity === 'overdue') return 'rgba(198,40,40,1)';
if (variables.apt_severity === 'error' || variables.apt_severity === 'scheduled') return 'rgba(230,81,0,1)';
if (variables.apt_severity === 'healthy') return 'rgba(46,125,50,1)';
return 'var(--secondary-text-color)';
]]]
label:
- grid-area: l
- justify-self: start
- padding-top: 3px
- font-size: 11px
- font-weight: 700
- letter-spacing: 0.02em
- opacity: 0.72
- white-space: normal
- text-align: left
- color: >
[[[
if (variables.apt_severity === 'overdue') return 'rgba(198,40,40,1)';
if (variables.apt_severity === 'error' || variables.apt_severity === 'scheduled') return 'rgba(230,81,0,1)';
if (variables.apt_severity === 'healthy') return 'rgba(46,125,50,1)';
return 'var(--secondary-text-color)';
]]]

@ -3,10 +3,10 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Home
# Clean layout inspired by Stitch wireframe (hosts, right-rail trends, storage, wifi, apt).
# Infrastructure View - operations summary
# Compact category health, active exceptions, datacenter, and network trends.
# -------------------------------------------------------------------
# Notes: Uses existing Infrastructure entities from Proxmox/UniFi/UPS/Docker hosts.
# Notes: Production exception-first Infrastructure home view.
######################################################################
title: Infrastructure
@ -15,6 +15,7 @@ type: sections
icon: mdi:view-dashboard-variant
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/home_sections.yaml
cards: []

@ -3,18 +3,19 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - ProxMox
# YAML-exported Lovelace dashboard (split into view files).
# Infrastructure View - Proxmox
# Host health and one-metric trends without duplicate KPI reporting.
# -------------------------------------------------------------------
# Notes: Exported from config/.storage/lovelace.dashboard_infrastructure view index 1.
# Notes: All graphs use a consistent 24-hour window.
######################################################################
title: ProxMox
path: proxmox
cards: []
type: sections
icon: mdi:server
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/proxmox_sections.yaml
max_columns: 4
cards: []

@ -3,12 +3,10 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Docker containers
# Related Issue: 1560, 1725
# Container status + restart controls (Portainer integration).
# Infrastructure View - Docker
# Exception-first host, update, volume, and workload health.
# -------------------------------------------------------------------
# Notes: Uses `binary_sensor.*_status` and `button.*_restart_container` entities (Portainer integration).
# Notes: 2026.5 Portainer volume prune controls are exposed through confirmed host maintenance popups; kill/recreate buttons are intentionally not exposed broadly.
# Notes: Complete healthy inventory is available from the hidden Docker Inventory subview.
######################################################################
title: Docker
@ -16,6 +14,8 @@ path: docker
type: sections
icon: mdi:docker
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/docker_containers_sections.yaml
max_columns: 4
cards: []

@ -0,0 +1,21 @@
######################################################################
# @CCOSTAN - Follow Me on X
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Network and External Services
# Combined WAN telemetry, dual Pi-hole operations, and website health.
# -------------------------------------------------------------------
# Notes: Detailed Pi-hole and website telemetry use hidden drill-down subviews.
######################################################################
title: Network
path: network
type: sections
icon: mdi:wan
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/network_sections.yaml
cards: []

@ -3,17 +3,20 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Pi-hole
# Dedicated Pi-hole view after Docker.
# Infrastructure Subview - Pi-hole Detail
# Large analytics and controls for both DNS nodes.
# -------------------------------------------------------------------
# Notes: Full-width sections layout for Pi-hole telemetry/control card.
# Notes: Hidden drill-down reached from the combined Network view.
######################################################################
title: Pi-hole
title: Pi-hole Details
path: pihole
type: sections
icon: mdi:pi-hole
subview: true
back_path: /dashboard-infrastructure/network
theme: default
badges: []
sections: !include /config/dashboards/infrastructure/partials/pihole_sections.yaml
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/pihole_detail_sections.yaml
cards: []

@ -4,17 +4,18 @@
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - MariaDB
# YAML-exported Lovelace dashboard (split into view files).
# Humanized KPIs, recorder health, capacity trends, and tuning details.
# -------------------------------------------------------------------
# Notes: Exported from config/.storage/lovelace.dashboard_infrastructure view index 5.
# Notes: Trend cards use a consistent seven-day window.
######################################################################
type: sections
title: MariaDB
path: mariadb
max_columns: 4
type: sections
icon: mdi:database
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/mariadb_sections.yaml
cards: []

@ -4,20 +4,18 @@
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Joanna
# Dispatch and activity telemetry for BearClaw/Joanna.
# Focused dispatch health, latest activity, diagnostics, and reset status.
# -------------------------------------------------------------------
# Notes: Uses telemetry from /api/bearclaw/status ingested by bearclaw.yaml.
# Notes: Shows Codex reset-credit expiry data from the 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
# Notes: Replaces the unbounded embedded logbook with a direct Activity link.
######################################################################
type: sections
title: Joanna
path: joanna
max_columns: 4
type: sections
icon: mdi:robot
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/joanna_sections.yaml
cards: []

@ -3,18 +3,20 @@
# For more info visit https://www.vcloudinfo.com/click-here
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
# -------------------------------------------------------------------
# Infrastructure View - Website Health
# Website uptime + domain expiry + certificate telemetry details.
# Infrastructure Subview - Website Details
# Current uptime, registration expiry, and actionable exceptions.
# -------------------------------------------------------------------
# Notes: Home view surfaces only exceptions; this view carries detail.
# Notes: Hidden drill-down reached from the combined Network view.
######################################################################
title: Website Health
title: Website Details
path: website-health
type: sections
icon: mdi:web-check
subview: true
back_path: /dashboard-infrastructure/network
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/website_health_sections.yaml
max_columns: 4
cards: []

@ -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 - Docker Inventory
# Full stack and container inventory separated from the operational view.
# -------------------------------------------------------------------
# Notes: Hidden subview reached from the Docker exceptions panel.
######################################################################
title: Docker Inventory
path: docker-inventory
type: sections
subview: true
icon: mdi:format-list-bulleted-square
theme: default
max_columns: 4
dense_section_placement: true
badges: []
sections: !include /config/dashboards/infrastructure/partials/docker_inventory_sections.yaml
cards: []

@ -24,6 +24,7 @@
not_home: grayscale(100%) opacity(50%)
Justin Dorm: grayscale(10%) opacity(50%)
Paige Home: grayscale(10%) opacity(50%)
Joyce Home: grayscale(100%) opacity(50%)
driving: grayscale(100%) opacity(50%)
- type: picture-entity
entity: person.stacey
@ -35,6 +36,7 @@
not_home: grayscale(100%) opacity(50%)
Justin Dorm: grayscale(10%) opacity(50%)
Paige Home: grayscale(10%) opacity(50%)
Joyce Home: grayscale(100%) opacity(50%)
gym: grayscale(100%) opacity(50%)
driving: grayscale(100%) opacity(50%)
- type: picture-entity
@ -47,6 +49,7 @@
not_home: grayscale(100%) opacity(50%)
Justin Dorm: grayscale(10%) opacity(50%)
Paige Home: grayscale(10%) opacity(50%)
Joyce Home: grayscale(100%) opacity(50%)
gym: grayscale(100%) opacity(50%)
driving: grayscale(100%) opacity(50%)
- type: picture-entity
@ -60,6 +63,7 @@
paige_work: grayscale(100%) opacity(50%)
Justin Dorm: grayscale(10%) opacity(50%)
Paige Home: grayscale(10%) opacity(50%)
Joyce Home: grayscale(100%) opacity(50%)
gym: grayscale(100%) opacity(50%)
driving: grayscale(100%) opacity(50%)
columns: 4

Loading…
Cancel
Save

Powered by TurnKey Linux.