###################################################################### # @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 - Home sections # Desktop-first infra overview: full-width hero, exceptions-first alerts. # ------------------------------------------------------------------- # Notes: Default/light theme only; no dark-mode specific styling. # Notes: Keep the first section full-width to avoid whitespace on desktop. ###################################################################### # ------------------------------------------------------------------- # Home hero (mandatory first full-width container) # ------------------------------------------------------------------- - 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 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: Alerts - type: custom:button-card template: bearstone_infra_list_row name: Network icon: mdi:access-point-network show_state: false tap_action: action: none - 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 variables: alert_kind: ap_zero min_age_s: 300 - 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 variables: alert_kind: ap_zero min_age_s: 300 - 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 variables: alert_kind: ap_zero min_age_s: 300 - 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 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}`; ]]] - type: custom:button-card template: bearstone_infra_alert_row entity: sensor.speedtest_ping name: WAN latency high icon: mdi:wan variables: alert_kind: disk_high threshold: 80 state_display: > [[[ return `${entity.state} ms`; ]]] - type: custom:button-card template: bearstone_infra_alert_row entity: switch.pi_hole name: DNS Pi-hole disabled icon: mdi:dns-outline variables: alert_kind: binary_off tap_action: action: navigate navigation_path: /dashboard-infrastructure/pihole state_display: > [[[ return `Switch ${entity.state}`; ]]] - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.pihole_status name: DNS Pi-hole service down icon: mdi:dns variables: alert_kind: binary_off tap_action: action: navigate navigation_path: /dashboard-infrastructure/pihole - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.vcloudinfo_com name: vcloudinfo.com is down icon: mdi:web-cancel variables: alert_kind: binary_off tap_action: action: navigate navigation_path: /dashboard-infrastructure/website-health - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.www_kingcrafthomes_com name: kingcrafthomes.com is down icon: mdi:web-cancel variables: alert_kind: binary_off tap_action: action: navigate navigation_path: /dashboard-infrastructure/website-health - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.bear_stone name: Bear Stone is down icon: mdi:web-cancel 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 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'; ]]] - 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 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'; ]]] - 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 - type: custom:button-card template: bearstone_infra_alert_row entity: binary_sensor.node_proxmox1_updates_packages name: Proxmox01 updates pending icon: mdi:package-up 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 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: sensor.disk_use_percent name: Home Assistant disk usage high icon: mdi:harddisk variables: alert_kind: disk_high threshold: 80 state_display: > [[[ return `${entity.state}% used`; ]]] - type: custom:button-card template: bearstone_infra_list_row name: Backup stale or failed icon: mdi:backup-restore state_display: > [[[ const s = states['sensor.dockerconfigs_backup_status']?.state ?? 'unknown'; const e = states['sensor.dockerconfigs_backup_error_message']?.state ?? ''; const d = states['sensor.dockerconfigs_backup_date']?.state; let ageText = 'n/a'; if (d && !['unknown','unavailable','none',''].includes(String(d).toLowerCase())) { const dt = new Date(d); if (!Number.isNaN(dt.getTime())) { const h = (Date.now() - dt.getTime()) / 3600000; ageText = `${h.toFixed(1)}h`; } } return `Age ${ageText} | ${s}${e ? ' | error' : ''}`; ]]] styles: card: - display: > [[[ const status = String(states['sensor.dockerconfigs_backup_status']?.state ?? '').toLowerCase(); const err = String(states['sensor.dockerconfigs_backup_error_message']?.state ?? '').toLowerCase(); const d = states['sensor.dockerconfigs_backup_date']?.state; let stale = false; if (d && !['unknown','unavailable','none',''].includes(String(d).toLowerCase())) { const dt = new Date(d); if (!Number.isNaN(dt.getTime())) stale = ((Date.now() - dt.getTime()) / 3600000) > 24; } const failed = status.includes('fail') || status.includes('error') || err.length > 0; return (stale || failed) ? 'block' : 'none'; ]]] - type: custom:button-card template: bearstone_infra_list_row name: Services icon: mdi:docker show_state: false tap_action: action: none - 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: - group: group.docker_monitored_containers options: type: custom:button-card template: bearstone_infra_container_row icon: mdi:docker exclude: - state: 'on' - 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 icon: mdi:thermometer hours_to_show: 96 points_per_hour: 1 line_width: 2 smoothing: true show: legend: false labels: false name: true icon: true state: true color_thresholds: - value: 50 color: '#f39c12' - value: 120 color: '#d35400' - value: 145 color: '#c0392b' entities: - entity: sensor.proxmox_garage_average_temperature name: Garage 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 entity: sensor.garage_ups_status name: UPS Status icon: mdi:battery-heart-variant - type: custom:button-card template: bearstone_infra_list_row entity: sensor.garage_ups_battery_charge name: UPS Battery icon: mdi:battery-70 state_display: > [[[ const v = Number(entity?.state); return Number.isFinite(v) ? `${v.toFixed(0)}%` : (entity?.state ?? 'unknown'); ]]] - type: custom:button-card template: bearstone_infra_list_row entity: sensor.garage_ups_battery_runtime name: Runtime Remaining icon: mdi:timer-outline state_display: > [[[ const secs = parseInt(entity?.state, 10); if (!Number.isFinite(secs)) return entity?.state ?? 'unknown'; const hours = Math.floor(secs / 3600); const mins = Math.floor((secs % 3600) / 60); return hours > 0 ? `${hours}h ${mins}m` : `${mins}m`; ]]] - type: custom:button-card template: bearstone_infra_list_row entity: sensor.garage_ups_status name: On Battery icon: mdi:battery-alert-variant-outline state_display: > [[[ const s = String(entity?.state || '').toUpperCase(); return s.includes('OB') ? 'Yes' : 'No'; ]]] # ------------------------------------------------------------------- # 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