Commit bd4d32bd12b0

Vincent Demeester <vincent@sbr.pm>
2025-12-15 14:19:40
feat(homelab): Add declarative Home Assistant dashboard configuration
- Enable version-controlled YAML dashboards for homelab services - Provide WebSocket API tooling for entity discovery and debugging - Establish foundation for centralized service monitoring Co-Authored-By: Claude <noreply@anthropic.com> Signed-off-by: Vincent Demeester <vincent@sbr.pm>
1 parent 0712ae7
home-assistant/views/automation.yaml
@@ -0,0 +1,120 @@
+# Automation & Devices Dashboard
+# Smart home devices, automations, and integrations
+
+title: Automation
+path: automation
+icon: mdi:home-automation
+badges: []
+
+cards:
+  # Automation header
+  - type: markdown
+    content: |
+      ## Home Automation
+      Smart devices, automations, and integrations
+
+  # Smart Devices
+  - type: entities
+    title: Smart Devices
+    show_header_toggle: false
+    entities:
+      # Add your actual smart home devices here
+      # Examples based on common integrations:
+      # Shield TV (if integrated via Android TV integration)
+      # - entity: media_player.shield_tv
+      #   name: Shield TV
+      #
+      # Lights
+      # - entity: light.living_room
+      #   name: Living Room
+      #
+      # Switches
+      # - entity: switch.office_lamp
+      #   name: Office Lamp
+
+  # Active Automations
+  - type: entities
+    title: Automations
+    show_header_toggle: false
+    entities:
+      # Add your automations here
+      # - entity: automation.morning_routine
+      #   name: Morning Routine
+      # - entity: automation.media_server_health_check
+      #   name: Media Server Health Check
+      # - entity: automation.backup_notification
+      #   name: Backup Notifications
+
+  # Syncthing Status
+  - type: markdown
+    content: |
+      ### Syncthing Sync Status
+      Monitor folder synchronization across devices
+
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Main sync folders
+      - type: entities
+        title: Sync Folders
+        entities:
+          # Add Syncthing folder sensors if available
+          # - entity: sensor.syncthing_org_sync_status
+          #   name: Org Files
+          # - entity: sensor.syncthing_documents_sync_status
+          #   name: Documents
+          # - entity: sensor.syncthing_sync_sync_status
+          #   name: General Sync
+          - type: section
+            label: Placeholder
+
+      # Device sync status
+      - type: entities
+        title: Connected Devices
+        entities:
+          # Add Syncthing device sensors
+          # - entity: binary_sensor.syncthing_kyushu
+          #   name: Kyushu (Laptop)
+          # - entity: binary_sensor.syncthing_aomi
+          #   name: Aomi (Desktop)
+          # - entity: binary_sensor.syncthing_hokkaido
+          #   name: Hokkaido (iPhone)
+          - type: section
+            label: Placeholder
+
+  # System Health
+  - type: markdown
+    content: |
+      ### System Health Checks
+      Configure automation for:
+      - Service availability monitoring
+      - Disk space alerts
+      - Backup completion notifications
+      - Certificate expiration warnings
+      - VPN connection status
+
+  # Quick Actions
+  - type: entities
+    title: Quick Actions
+    entities:
+      # Add useful scripts and scenes
+      # - entity: script.restart_media_services
+      #   name: Restart Media Services
+      # - entity: script.run_backup
+      #   name: Run Backup
+      # - entity: scene.movie_time
+      #   name: Movie Mode
+      - type: section
+        label: Configure scripts and scenes
+
+  # Notifications
+  - type: markdown
+    content: |
+      ### Notifications
+      Configure ntfy.sh notifications for:
+      - Download completions (Sonarr/Radarr)
+      - Media requests (Jellyseerr)
+      - System alerts (disk space, health checks)
+      - Backup status (audible-sync, PostgreSQL)
+      - Certificate renewals
home-assistant/views/downloads.yaml
@@ -0,0 +1,162 @@
+# Download Management Dashboard
+# Sonarr, Radarr, Lidarr, Bazarr, Prowlarr, Transmission
+
+title: Downloads
+path: downloads
+icon: mdi:download-circle
+badges: []
+
+cards:
+  # Downloads header
+  - type: markdown
+    content: |
+      ## Download Management
+      *arr suite for media automation and torrents
+
+  # Transmission - Torrent Client
+  - type: entities
+    title: Transmission (Torrent Client)
+    show_header_toggle: false
+    entities:
+      - entity: binary_sensor.transmission_status
+        name: Status
+      # Add actual transmission sensors:
+      # - entity: sensor.transmission_active_torrents
+      #   name: Active Torrents
+      # - entity: sensor.transmission_download_speed
+      #   name: Download Speed
+      # - entity: sensor.transmission_upload_speed
+      #   name: Upload Speed
+      # - entity: sensor.transmission_paused_torrents
+      #   name: Paused
+      # - entity: switch.transmission_turtle_mode
+      #   name: Speed Limit Mode
+    footer:
+      type: buttons
+      entities:
+        - entity: binary_sensor.transmission_status
+          name: Open
+          tap_action:
+            action: url
+            url_path: https://transmission.sbr.pm
+
+  # *arr Services Grid
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Sonarr - TV Shows
+      - type: entities
+        title: Sonarr (TV Shows)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.sonarr_status
+            name: Status
+          # Add Sonarr sensors:
+          # - entity: sensor.sonarr_wanted
+          #   name: Wanted
+          # - entity: sensor.sonarr_queue
+          #   name: Queue
+          # - entity: sensor.sonarr_series
+          #   name: Series Count
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.sonarr_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://sonarr.sbr.pm
+
+      # Radarr - Movies
+      - type: entities
+        title: Radarr (Movies)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.radarr_status
+            name: Status
+          # Add Radarr sensors:
+          # - entity: sensor.radarr_wanted
+          #   name: Wanted
+          # - entity: sensor.radarr_queue
+          #   name: Queue
+          # - entity: sensor.radarr_movies
+          #   name: Movie Count
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.radarr_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://radarr.sbr.pm
+
+      # Lidarr - Music
+      - type: entities
+        title: Lidarr (Music)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.lidarr_status
+            name: Status
+          # Add Lidarr sensors:
+          # - entity: sensor.lidarr_wanted
+          #   name: Wanted
+          # - entity: sensor.lidarr_queue
+          #   name: Queue
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.lidarr_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://lidarr.sbr.pm
+
+      # Bazarr - Subtitles
+      - type: entities
+        title: Bazarr (Subtitles)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.bazarr_status
+            name: Status
+          # Add Bazarr sensors:
+          # - entity: sensor.bazarr_missing_subtitles
+          #   name: Missing Subtitles
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.bazarr_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://bazarr.sbr.pm
+
+  # Prowlarr - Indexer Manager
+  - type: entities
+    title: Prowlarr (Indexer Manager)
+    show_header_toggle: false
+    entities:
+      - entity: binary_sensor.prowlarr_status
+        name: Status
+      # Add Prowlarr sensors:
+      # - entity: sensor.prowlarr_indexers
+      #   name: Active Indexers
+      # - entity: sensor.prowlarr_health_issues
+      #   name: Health Issues
+    footer:
+      type: buttons
+      entities:
+        - entity: binary_sensor.prowlarr_status
+          name: Open
+          tap_action:
+            action: url
+            url_path: https://prowlarr.sbr.pm
+
+  # Download queue overview
+  - type: markdown
+    content: |
+      ### Queue Summary
+      Configure automation to track:
+      - Total items downloading
+      - Download completion notifications
+      - Failed download alerts
home-assistant/views/infrastructure.yaml
@@ -0,0 +1,215 @@
+# Infrastructure Dashboard
+# Servers, monitoring, automation tools
+
+title: Infrastructure
+path: infrastructure
+icon: mdi:server-network
+badges: []
+
+cards:
+  # Infrastructure header
+  - type: markdown
+    content: |
+      ## Infrastructure & Monitoring
+      System status, automation, and services
+
+  # Primary Servers
+  - type: grid
+    columns: 3
+    square: false
+    cards:
+      # Rhea - Main Media Server
+      - type: vertical-stack
+        cards:
+          - type: markdown
+            content: |
+              ### Rhea
+              **Media & Downloads**
+              192.168.1.50
+          - type: glance
+            entities:
+              # Add actual system sensors:
+              # - entity: sensor.rhea_cpu_usage
+              - entity: binary_sensor.rhea_status
+                name: Status
+            show_name: true
+            columns: 1
+
+      # Sakhalin - Services
+      - type: vertical-stack
+        cards:
+          - type: markdown
+            content: |
+              ### Sakhalin
+              **Services & Tools**
+              192.168.1.70
+          - type: glance
+            entities:
+              - entity: binary_sensor.sakhalin_status
+                name: Status
+            columns: 1
+
+      # Aion - Music Server
+      - type: vertical-stack
+        cards:
+          - type: markdown
+            content: |
+              ### Aion
+              **Music Streaming**
+              192.168.1.49
+          - type: glance
+            entities:
+              - entity: binary_sensor.aion_status
+                name: Status
+            columns: 1
+
+  # Monitoring & Management
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Grafana - Monitoring
+      - type: entities
+        title: Grafana (Monitoring)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.grafana_status
+            name: Status
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.grafana_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://grafana.sbr.pm
+
+      # Traefik - Reverse Proxy
+      - type: entities
+        title: Traefik (Reverse Proxy)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.traefik_status
+            name: Status
+          # Add Traefik sensors:
+          # - entity: sensor.traefik_routers_count
+          #   name: Active Routers
+          # - entity: sensor.traefik_services_count
+          #   name: Services
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.traefik_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://traefik.sbr.pm
+
+  # Productivity & Automation
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Paperless - Document Management
+      - type: entities
+        title: Paperless (Documents)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.paperless_status
+            name: Status
+          # Add Paperless sensors:
+          # - entity: sensor.paperless_document_count
+          #   name: Documents
+          # - entity: sensor.paperless_inbox_count
+          #   name: Inbox
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.paperless_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://paperless.sbr.pm
+
+      # N8N - Workflow Automation
+      - type: entities
+        title: N8N (Workflows)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.n8n_status
+            name: Status
+          # Add N8N sensors:
+          # - entity: sensor.n8n_active_workflows
+          #   name: Active Workflows
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.n8n_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://n8n.sbr.pm
+
+      # Kiwix - Offline Wikipedia
+      - type: entities
+        title: Kiwix (Offline Content)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.kiwix_status
+            name: Status
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.kiwix_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://kiwix.sbr.pm
+
+      # Linkwarden - Bookmarks
+      - type: entities
+        title: Linkwarden (Bookmarks)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.linkwarden_status
+            name: Status
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.linkwarden_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://links.sbr.pm
+
+  # Network & Storage
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Athena - WebDAV
+      - type: entities
+        title: Athena (WebDAV Storage)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.athena_status
+            name: Status
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.athena_status
+              name: Open WebDAV
+              tap_action:
+                action: url
+                url_path: https://dav.sbr.pm
+
+      # Demeter - MQTT Broker
+      - type: entities
+        title: Demeter (MQTT Broker)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.demeter_status
+            name: Status
+          # Add MQTT sensors:
+          # - entity: sensor.mqtt_connected_clients
+          #   name: Connected Clients
home-assistant/views/media.yaml
@@ -0,0 +1,148 @@
+# Media Services Dashboard
+# Jellyfin, Immich, Navidrome, Audiobookshelf, Jellyseerr
+
+title: Media
+path: media
+icon: mdi:play-circle
+badges: []
+
+cards:
+  # Media header
+  - type: markdown
+    content: |
+      ## Media Services
+      Streaming, photos, music, and audiobooks
+
+  # Media service status
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Jellyfin - Media Server
+      - type: entities
+        title: Jellyfin (Media Server)
+        show_header_toggle: false
+        entities:
+          - type: attribute
+            entity: binary_sensor.jellyfin_status
+            attribute: url
+            name: URL
+          - entity: binary_sensor.jellyfin_status
+            name: Status
+          # Add actual sensors when available:
+          # - entity: sensor.jellyfin_active_streams
+          #   name: Active Streams
+          # - entity: sensor.jellyfin_movie_count
+          #   name: Movies
+          # - entity: sensor.jellyfin_series_count
+          #   name: TV Shows
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.jellyfin_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://jellyfin.sbr.pm
+
+      # Immich - Photo Management
+      - type: entities
+        title: Immich (Photos)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.immich_status
+            name: Status
+          # Add actual sensors:
+          # - entity: sensor.immich_photo_count
+          #   name: Photos
+          # - entity: sensor.immich_video_count
+          #   name: Videos
+          # - entity: sensor.immich_storage_usage
+          #   name: Storage Used
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.immich_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://immich.sbr.pm
+
+  # Music and Audiobooks
+  - type: grid
+    columns: 2
+    square: false
+    cards:
+      # Navidrome - Music Streaming
+      - type: entities
+        title: Navidrome (Music)
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.navidrome_status
+            name: Status
+          # Add actual sensors:
+          # - entity: sensor.navidrome_artist_count
+          #   name: Artists
+          # - entity: sensor.navidrome_album_count
+          #   name: Albums
+          # - entity: sensor.navidrome_track_count
+          #   name: Tracks
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.navidrome_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://music.sbr.pm
+
+      # Audiobookshelf - Podcasts & Audiobooks
+      - type: entities
+        title: Audiobookshelf
+        show_header_toggle: false
+        entities:
+          - entity: binary_sensor.audiobookshelf_status
+            name: Status
+          # Add actual sensors:
+          # - entity: sensor.audiobookshelf_audiobook_count
+          #   name: Audiobooks
+          # - entity: sensor.audiobookshelf_podcast_count
+          #   name: Podcasts
+        footer:
+          type: buttons
+          entities:
+            - entity: binary_sensor.audiobookshelf_status
+              name: Open
+              tap_action:
+                action: url
+                url_path: https://podcasts.sbr.pm
+
+  # Jellyseerr - Media Requests
+  - type: entities
+    title: Jellyseerr (Requests)
+    show_header_toggle: false
+    entities:
+      - entity: binary_sensor.jellyseerr_status
+        name: Status
+      # Add actual sensors:
+      # - entity: sensor.jellyseerr_pending_requests
+      #   name: Pending Requests
+      # - entity: sensor.jellyseerr_total_requests
+      #   name: Total Requests
+    footer:
+      type: buttons
+      entities:
+        - entity: binary_sensor.jellyseerr_status
+          name: Open
+          tap_action:
+            action: url
+            url_path: https://jellyseerr.sbr.pm
+
+  # Recent activity (if available)
+  - type: markdown
+    content: |
+      ### Recent Activity
+      Configure sensors for:
+      - Recently added movies/shows (Jellyfin)
+      - Latest photo uploads (Immich)
+      - New audiobooks (Audiobookshelf)
home-assistant/views/overview.yaml
@@ -0,0 +1,152 @@
+# Overview - Home Dashboard
+# Default view showing high-level status of all services
+
+title: Overview
+path: overview
+icon: mdi:view-dashboard
+badges: []
+
+cards:
+  # Welcome header
+  - type: markdown
+    content: |
+      # Homelab Dashboard
+      Welcome to your self-hosted infrastructure.
+
+  # Quick status overview
+  - type: vertical-stack
+    cards:
+      - type: glance
+        title: Core Infrastructure
+        entities:
+          # Add your actual entities here
+          # Examples:
+          - entity: binary_sensor.rhea_status
+            name: Rhea (Media)
+          - entity: binary_sensor.sakhalin_status
+            name: Sakhalin (Services)
+          - entity: binary_sensor.aion_status
+            name: Aion (Music)
+          - entity: binary_sensor.athena_status
+            name: Athena (Storage)
+          - entity: binary_sensor.demeter_status
+            name: Demeter (MQTT)
+        show_name: true
+        show_state: true
+        columns: 5
+
+      - type: glance
+        title: Network
+        entities:
+          # Add actual network entities
+          - entity: binary_sensor.traefik_status
+            name: Traefik
+          - entity: binary_sensor.wireguard_status
+            name: VPN
+        columns: 2
+
+  # Service status grid
+  - type: grid
+    columns: 3
+    square: false
+    cards:
+      # Media Services
+      - type: button
+        name: Jellyfin
+        icon: mdi:movie-open
+        tap_action:
+          action: url
+          url_path: https://jellyfin.sbr.pm
+        hold_action:
+          action: none
+        entity: binary_sensor.jellyfin_status
+        show_state: true
+
+      - type: button
+        name: Immich
+        icon: mdi:image-multiple
+        tap_action:
+          action: url
+          url_path: https://immich.sbr.pm
+        entity: binary_sensor.immich_status
+        show_state: true
+
+      - type: button
+        name: Navidrome
+        icon: mdi:music
+        tap_action:
+          action: url
+          url_path: https://music.sbr.pm
+        entity: binary_sensor.navidrome_status
+        show_state: true
+
+      # Download Management
+      - type: button
+        name: Transmission
+        icon: mdi:download
+        tap_action:
+          action: url
+          url_path: https://transmission.sbr.pm
+        entity: binary_sensor.transmission_status
+        show_state: true
+
+      - type: button
+        name: Sonarr
+        icon: mdi:television
+        tap_action:
+          action: url
+          url_path: https://sonarr.sbr.pm
+        entity: binary_sensor.sonarr_status
+        show_state: true
+
+      - type: button
+        name: Radarr
+        icon: mdi:filmstrip
+        tap_action:
+          action: url
+          url_path: https://radarr.sbr.pm
+        entity: binary_sensor.radarr_status
+        show_state: true
+
+  # System resources
+  - type: horizontal-stack
+    cards:
+      - type: gauge
+        name: Rhea CPU
+        entity: sensor.rhea_cpu_usage
+        min: 0
+        max: 100
+        severity:
+          green: 0
+          yellow: 60
+          red: 85
+
+      - type: gauge
+        name: Rhea Memory
+        entity: sensor.rhea_memory_usage
+        min: 0
+        max: 100
+        severity:
+          green: 0
+          yellow: 70
+          red: 90
+
+      - type: gauge
+        name: Rhea Disk
+        entity: sensor.rhea_disk_usage
+        min: 0
+        max: 100
+        severity:
+          green: 0
+          yellow: 75
+          red: 90
+
+  # Quick actions
+  - type: entities
+    title: Quick Actions
+    entities:
+      # Add your automation entities
+      - entity: script.restart_jellyfin
+        name: Restart Jellyfin
+      - entity: script.restart_transmission
+        name: Restart Transmission
home-assistant/.gitignore
@@ -0,0 +1,43 @@
+# Gitignore for Home Assistant Dashboard Config
+
+# Secrets file (NEVER commit this!)
+secrets.yaml
+
+# Home Assistant database and logs
+home-assistant.log
+home-assistant_v2.db*
+*.db-shm
+*.db-wal
+
+# Backup files
+*.backup
+
+# Compiled Python files
+*.pyc
+__pycache__/
+
+# macOS
+.DS_Store
+
+# Editor files
+.vscode/
+.idea/
+*.swp
+*.swo
+*~
+
+# UUID cache
+.uuid
+
+# Storage
+.storage/
+.cloud/
+
+# Dependencies
+deps/
+
+# Test files
+*.test
+
+# Temporary files
+*.tmp
home-assistant/configuration-example.yaml
@@ -0,0 +1,157 @@
+# Example Home Assistant configuration.yaml additions
+# Copy relevant sections to your actual Home Assistant configuration
+
+# Enable YAML dashboard mode
+lovelace:
+  mode: yaml
+  # Optionally add custom resources (HACS cards)
+  resources:
+    # Uncomment and install via HACS:
+    # - url: /hacsfiles/mini-graph-card/mini-graph-card-bundle.js
+    #   type: module
+    # - url: /hacsfiles/button-card/button-card.js
+    #   type: module
+
+# RESTful sensors for service status monitoring
+# These use HTTP ping to check if services are available
+
+rest:
+  # Jellyfin
+  - resource: https://jellyfin.sbr.pm/health
+    scan_interval: 60
+    binary_sensor:
+      - name: "Jellyfin Status"
+        value_template: "{{ value_json.status == 'Healthy' or value == 'OK' }}"
+        device_class: connectivity
+
+  # Immich
+  - resource: https://immich.sbr.pm/api/server-info/ping
+    scan_interval: 60
+    binary_sensor:
+      - name: "Immich Status"
+        value_template: "{{ value_json.res == 'pong' or is_state('binary_sensor.immich_status', 'on') }}"
+        device_class: connectivity
+
+  # Transmission
+  - resource: https://transmission.sbr.pm
+    scan_interval: 60
+    binary_sensor:
+      - name: "Transmission Status"
+        value_template: "{{ true if value else false }}"
+        device_class: connectivity
+
+  # Sonarr
+  - resource: https://sonarr.sbr.pm/api/v3/health
+    scan_interval: 60
+    headers:
+      X-Api-Key: !secret sonarr_api_key
+    binary_sensor:
+      - name: "Sonarr Status"
+        value_template: "{{ is_state('binary_sensor.sonarr_status', 'on') or value }}"
+        device_class: connectivity
+
+  # Radarr
+  - resource: https://radarr.sbr.pm/api/v3/health
+    scan_interval: 60
+    headers:
+      X-Api-Key: !secret radarr_api_key
+    binary_sensor:
+      - name: "Radarr Status"
+        value_template: "{{ is_state('binary_sensor.radarr_status', 'on') or value }}"
+        device_class: connectivity
+
+  # Prowlarr
+  - resource: https://prowlarr.sbr.pm/api/v1/health
+    scan_interval: 60
+    headers:
+      X-Api-Key: !secret prowlarr_api_key
+    binary_sensor:
+      - name: "Prowlarr Status"
+        value_template: "{{ is_state('binary_sensor.prowlarr_status', 'on') or value }}"
+        device_class: connectivity
+
+  # Grafana
+  - resource: https://grafana.sbr.pm/api/health
+    scan_interval: 120
+    binary_sensor:
+      - name: "Grafana Status"
+        value_template: "{{ value_json.database == 'ok' or is_state('binary_sensor.grafana_status', 'on') }}"
+        device_class: connectivity
+
+  # Traefik
+  - resource: https://traefik.sbr.pm/api/overview
+    scan_interval: 60
+    binary_sensor:
+      - name: "Traefik Status"
+        value_template: "{{ value or is_state('binary_sensor.traefik_status', 'on') }}"
+        device_class: connectivity
+
+# Alternative: Use the ping integration for simple connectivity checks
+# This is simpler but only checks if the host responds to ping
+binary_sensor:
+  - platform: ping
+    name: "Rhea Server"
+    host: rhea.home
+    count: 2
+    scan_interval: 60
+
+  - platform: ping
+    name: "Sakhalin Server"
+    host: sakhalin.home
+    count: 2
+    scan_interval: 60
+
+  - platform: ping
+    name: "Aion Server"
+    host: aion.home
+    count: 2
+    scan_interval: 60
+
+  - platform: ping
+    name: "Athena Server"
+    host: athena.home
+    count: 2
+    scan_interval: 60
+
+  - platform: ping
+    name: "Demeter Server"
+    host: demeter.home
+    count: 2
+    scan_interval: 60
+
+# Notifications via ntfy.sh
+notify:
+  - name: ntfy_homelab
+    platform: rest
+    resource: https://ntfy.sbr.pm/homelab
+    method: POST_JSON
+    headers:
+      Title: "Home Assistant"
+    message_param_name: message
+
+# Optional: Sonarr/Radarr integrations via HACS
+# Install "Sonarr Upcoming Media" and "Radarr Upcoming Media" from HACS
+# Then configure:
+# sonarr_upcoming_media:
+#   api_key: !secret sonarr_api_key
+#   host: sonarr.sbr.pm
+#   port: 443
+#   ssl: true
+#   days: 7
+
+# radarr_upcoming_media:
+#   api_key: !secret radarr_api_key
+#   host: radarr.sbr.pm
+#   port: 443
+#   ssl: true
+#   days: 7
+
+# Optional: System monitor for local HA server
+sensor:
+  - platform: systemmonitor
+    resources:
+      - type: disk_use_percent
+        arg: /
+      - type: memory_use_percent
+      - type: processor_use
+      - type: last_boot
home-assistant/get-entities.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env -S uv run
+# /// script
+# requires-python = ">=3.11"
+# dependencies = [
+#     "websockets",
+# ]
+# ///
+"""
+Get Home Assistant entities via WebSocket API.
+Usage: ./get-ha-entities.py <token>
+"""
+
+import asyncio
+import websockets
+import json
+import sys
+from collections import defaultdict
+
+HA_WS = "ws://hass.home:8123/api/websocket"
+
+
+async def get_entities(token):
+    try:
+        async with websockets.connect(HA_WS) as ws:
+            # Receive auth_required
+            auth_req = json.loads(await ws.recv())
+            version = auth_req.get('ha_version', 'unknown')
+            print(f"Connected to Home Assistant {version}")
+
+            # Send authentication
+            await ws.send(json.dumps({
+                "type": "auth",
+                "access_token": token
+            }))
+
+            # Receive auth response
+            auth_resp = json.loads(await ws.recv())
+
+            if auth_resp["type"] != "auth_ok":
+                msg = auth_resp.get('message', 'Unknown error')
+                print(f"โŒ Authentication failed: {msg}")
+                return
+
+            print("โœ… Authenticated successfully!\n")
+
+            # Get all states
+            await ws.send(json.dumps({
+                "id": 1,
+                "type": "get_states"
+            }))
+
+            response = json.loads(await ws.recv())
+
+            if not response.get("success"):
+                print(f"โŒ Error getting states: {response}")
+                return
+
+            states = response["result"]
+
+            # Group by domain
+            by_domain = defaultdict(list)
+            for state in states:
+                domain = state["entity_id"].split(".")[0]
+                by_domain[domain].append(state)
+
+            # Display summary
+            print(f"๐Ÿ“Š Total entities: {len(states)}")
+            print(f"๐Ÿ“ Domains: {len(by_domain)}\n")
+
+            # Show each domain
+            for domain in sorted(by_domain.keys()):
+                entities = by_domain[domain]
+                print(f"=== {domain} ({len(entities)} entities) ===")
+
+                for entity in sorted(entities, key=lambda x: x["entity_id"]):
+                    entity_id = entity["entity_id"]
+                    state = entity["state"]
+                    attrs = entity.get("attributes", {})
+                    friendly_name = attrs.get("friendly_name", entity_id)
+                    unit = attrs.get("unit_of_measurement", "")
+
+                    state_display = f"{state} {unit}".strip()
+                    name = friendly_name
+                    print(f"  โ€ข {name} ({entity_id}): {state_display}")
+
+                print()
+
+    except websockets.exceptions.WebSocketException as e:
+        print(f"โŒ WebSocket error: {e}")
+        print(f"   Make sure Home Assistant is accessible at {HA_WS}")
+    except Exception as e:
+        print(f"โŒ Error: {e}")
+
+
+def main():
+    if len(sys.argv) < 2:
+        print("Usage: ./get-ha-entities.py <long-lived-access-token>")
+        print("")
+        print("Create a token in Home Assistant:")
+        print("  Profile โ†’ Long-Lived Access Tokens โ†’ Create Token")
+        sys.exit(1)
+
+    token = sys.argv[1]
+    asyncio.run(get_entities(token))
+
+
+if __name__ == "__main__":
+    main()
home-assistant/README.md
@@ -0,0 +1,123 @@
+# Home Assistant Dashboard Configuration
+
+This directory contains declarative YAML dashboard configurations for Home Assistant.
+
+## Structure
+
+```
+home-assistant/
+โ”œโ”€โ”€ ui-lovelace.yaml           # Main dashboard file (entry point)
+โ”œโ”€โ”€ views/                     # Dashboard views (pages)
+โ”‚   โ”œโ”€โ”€ overview.yaml          # Home overview
+โ”‚   โ”œโ”€โ”€ media.yaml             # Media services
+โ”‚   โ”œโ”€โ”€ downloads.yaml         # Download management
+โ”‚   โ”œโ”€โ”€ infrastructure.yaml    # Infrastructure monitoring
+โ”‚   โ””โ”€โ”€ automation.yaml        # Automation & devices
+โ”œโ”€โ”€ cards/                     # Reusable card components
+โ”‚   โ”œโ”€โ”€ media-cards.yaml
+โ”‚   โ”œโ”€โ”€ download-cards.yaml
+โ”‚   โ””โ”€โ”€ infrastructure-cards.yaml
+โ”œโ”€โ”€ configuration-example.yaml # Example sensor configurations
+โ”œโ”€โ”€ secrets-example.yaml       # Example secrets file
+โ””โ”€โ”€ get-entities.py            # Script to retrieve entities via API
+```
+
+## Installation
+
+### 1. Enable YAML Mode in Home Assistant
+
+Add to your Home Assistant `configuration.yaml`:
+
+```yaml
+lovelace:
+  mode: yaml
+  resources:
+    # Add any custom Lovelace cards here
+    - url: /local/custom-cards/mini-graph-card-bundle.js
+      type: module
+```
+
+### 2. Link Dashboard Files
+
+Copy or symlink the `ui-lovelace.yaml` file to your Home Assistant config directory:
+
+```bash
+# If Home Assistant is running on hass (192.168.1.181)
+ssh hass.home
+cd /config  # or wherever your HA config lives
+ln -s /path/to/this/repo/home-assistant/ui-lovelace.yaml .
+```
+
+Or copy the entire `home-assistant/` directory into your Home Assistant config.
+
+### 3. Reload Dashboard
+
+In Home Assistant:
+- Go to Settings โ†’ Dashboards
+- Click the three dots โ†’ "Reload resources"
+- Or restart Home Assistant
+
+## Customization
+
+### Adding Services
+
+Edit the relevant view file in `views/` to add new services.
+
+### Modifying Layout
+
+The dashboard uses a grid layout with responsive columns. Adjust `grid_columns` and `grid_min_rows` in view files.
+
+### Custom Cards
+
+Install custom cards via HACS (Home Assistant Community Store) and add them to the `resources` section in `configuration.yaml`.
+
+## Recommended Custom Cards
+
+- **mini-graph-card**: Beautiful graphs for sensors
+- **button-card**: Highly customizable buttons
+- **layout-card**: Advanced layout control
+- **auto-entities**: Dynamic card generation
+
+## Version Control
+
+These files are version controlled in the main homelab repository. Changes should be:
+
+1. Made in this repository
+2. Tested locally
+3. Deployed to Home Assistant
+4. Committed with descriptive messages
+
+## API Access
+
+### Retrieving Entities
+
+Use the `get-entities.py` script to retrieve all entities from Home Assistant via the WebSocket API:
+
+```bash
+# Create a long-lived access token in Home Assistant:
+# Profile โ†’ Long-Lived Access Tokens โ†’ Create Token
+
+# Run the script
+./get-entities.py YOUR_TOKEN_HERE
+```
+
+The script will:
+- Connect to Home Assistant at `hass.home:8123`
+- Authenticate with your token
+- Retrieve all entities grouped by domain
+- Display entity states, friendly names, and units
+
+This is useful for:
+- Discovering available entities for dashboard configuration
+- Debugging sensor configurations
+- Monitoring entity states
+- Generating entity lists for automations
+
+See the [Home Assistant API documentation](https://developers.home-assistant.io/docs/api/websocket/) for more details.
+
+## Notes
+
+- Service URLs use the `sbr.pm` domain (accessible via Traefik)
+- All services are HTTPS via Traefik reverse proxy
+- Monitor availability with `ping` sensors
+- Add API keys for service widgets where needed
home-assistant/secrets-example.yaml
@@ -0,0 +1,31 @@
+# secrets.yaml - Example file
+# Copy to your Home Assistant config directory as secrets.yaml
+# Fill in actual API keys from your services
+
+# *arr Services API Keys
+# Get these from Settings โ†’ General โ†’ Security in each service
+sonarr_api_key: YOUR_SONARR_API_KEY_HERE
+radarr_api_key: YOUR_RADARR_API_KEY_HERE
+lidarr_api_key: YOUR_LIDARR_API_KEY_HERE
+prowlarr_api_key: YOUR_PROWLARR_API_KEY_HERE
+bazarr_api_key: YOUR_BAZARR_API_KEY_HERE
+
+# Jellyfin API Key (if using Jellyfin integration)
+jellyfin_api_key: YOUR_JELLYFIN_API_KEY_HERE
+
+# Navidrome
+navidrome_username: YOUR_USERNAME
+navidrome_password: YOUR_PASSWORD
+
+# Audiobookshelf API Token
+audiobookshelf_token: YOUR_AUDIOBOOKSHELF_TOKEN_HERE
+
+# Transmission
+transmission_username: transmission
+transmission_password: transmission
+
+# Grafana
+grafana_token: YOUR_GRAFANA_TOKEN_HERE
+
+# ntfy.sh topic (if different from default)
+ntfy_topic: homelab
home-assistant/ui-lovelace.yaml
@@ -0,0 +1,19 @@
+# Home Assistant Dashboard - Main Configuration
+# Entry point for the declarative Lovelace UI
+
+# Resources (custom cards from HACS)
+resources: []
+  # Example custom cards:
+  # - url: /hacsfiles/mini-graph-card/mini-graph-card-bundle.js
+  #   type: module
+
+# Dashboard title
+title: Homelab
+
+# Views (pages)
+views:
+  - !include views/overview.yaml
+  - !include views/media.yaml
+  - !include views/downloads.yaml
+  - !include views/infrastructure.yaml
+  - !include views/automation.yaml