home-assistant
Home Assistant Complete Management & Development
The ultimate comprehensive skill for Home Assistant covering:
- Administration: Configuration analysis, REST API, automation management, entity monitoring
- Wireless Protocols: Zigbee (ZHA + Zigbee2MQTT), Z-Wave JS, Thread, Matter
- ESPHome: Full device building with YAML, sensors, GPIO, I2C, SPI, custom components
- Troubleshooting: Log analysis, automation traces, debug logging, performance tuning
- Database: Recorder optimization, MariaDB/PostgreSQL, entity filtering, long-term statistics
- Security: SSL/TLS, authentication, IP banning, secrets management
- Development: Custom integrations, advanced Jinja2 templating, config flows
- Dashboards: HACS cards, themes, animations, responsive layouts, floor plans
Session Configuration
CRITICAL: Connection Setup
When the user first requests a Home Assistant operation, collect the necessary connection details:
I need Home Assistant connection details. Please provide what applies:
**For Configuration File Access:**
1. **Config Path**: Local path OR SSH details to HA config directory
- Local: /config, /homeassistant, or custom path
- SSH: user@host:/path/to/config
**For REST API Access (optional but recommended):**
2. **HA URL**: (e.g., http://192.168.1.100:8123 or https://ha.example.com)
3. **Long-Lived Access Token**: (Create at Profile > Security > Long-Lived Access Tokens)
Example response:
- Config Path: /home/user/homeassistant
- HA URL: http://192.168.1.100:8123
- Token: eyJhbGciOiJIUzI1NiIsInR5cCI6Ikp...
After receiving details:
- Store them in working memory for the session
- Use for ALL subsequent HA operations without re-prompting
- NEVER write tokens/credentials to files or logs
Cross-Platform SSH Support
CRITICAL: SSH authentication handling differs by platform.
When accessing Home Assistant configuration via SSH, the method depends on your operating system.
Authentication Priority
- SSH Keys (RECOMMENDED) - Works on all platforms without additional tools
- REST API - Works everywhere, preferred for most operations
- Python SSH Helper - Works on all platforms with
paramikolibrary - sshpass - Linux/macOS only (not available on Windows)
Platform-Specific SSH Commands
With SSH Keys (All Platforms):
# Direct SSH command - keys are used automatically if configured
ssh -o StrictHostKeyChecking=accept-new user@<HA-HOST> "cat /config/configuration.yaml"
With Password - Windows:
# Use Python helper (paramiko-based)
python scripts/ssh_helper.py --host <HA-HOST> --user homeassistant --password "<password>" --command "cat /config/configuration.yaml"
# Download config file
python scripts/ssh_helper.py --host <HA-HOST> --user homeassistant --password "<password>" --download /config/configuration.yaml --local-path ./configuration.yaml
With Password - macOS/Linux:
# Using sshpass (if installed)
sshpass -p '<password>' ssh -o StrictHostKeyChecking=accept-new user@<HA-HOST> "cat /config/configuration.yaml"
# Or use Python helper (cross-platform)
python3 scripts/ssh_helper.py --host <HA-HOST> --user homeassistant --password "<password>" --command "cat /config/configuration.yaml"
Setting Up SSH Keys for Home Assistant
# Generate key if you don't have one
ssh-keygen -t ed25519 -C "ha-admin"
# For Home Assistant OS (via Terminal add-on or SSH add-on)
# Add your public key to the authorized_keys in the SSH add-on configuration
SSH Access by Installation Type
| Installation | SSH Available | Username | Config Path |
|---|---|---|---|
| Home Assistant OS | Via SSH Add-on | root |
/config/ |
| Home Assistant Container | Via Docker host | Host user | Mounted path |
| Home Assistant Core | Via host SSH | User running HA | ~/.homeassistant/ |
Recommendation: Use the REST API for most operations - it's cross-platform and doesn't require SSH setup.
Quick Reference
File Structure (Home Assistant OS)
/config/ # Main configuration directory
├── configuration.yaml # Primary configuration file
├── automations.yaml # UI-created automations
├── scripts.yaml # UI-created scripts
├── scenes.yaml # UI-created scenes
├── secrets.yaml # Sensitive credentials (NEVER share)
├── known_devices.yaml # Device tracker known devices
├── customize.yaml # Entity customizations
├── home-assistant.log # Current session log
├── home-assistant.log.1 # Previous session log
├── .storage/ # Internal state storage (DO NOT EDIT)
│ ├── auth # Authentication data
│ ├── core.config_entries # Integration configs
│ ├── core.entity_registry # Entity registry
│ ├── core.device_registry # Device registry
│ └── lovelace # Dashboard configs
├── custom_components/ # HACS and manual integrations
├── www/ # Static web assets
├── blueprints/ # Automation blueprints
│ ├── automation/
│ └── script/
├── packages/ # Package configurations
└── tts/ # Text-to-speech cache
REST API Base
Base URL: http://<HA_HOST>:8123/api/
Auth Header: Authorization: Bearer <LONG_LIVED_TOKEN>
Content-Type: application/json
Configuration Analysis
Reading Configuration Files
# Read main configuration
cat /config/configuration.yaml
# Check for syntax errors (via API)
curl -X POST "http://HA_HOST:8123/api/config/core/check_config" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
# List all YAML files
find /config -name "*.yaml" -type f
# Search for specific configuration
grep -r "sensor:" /config/*.yaml
grep -r "automation:" /config/*.yaml
Configuration Best Practices Check
When analyzing configuration, look for:
- Secrets Usage: Ensure sensitive data uses
!secretreferences - Split Configuration: Large configs should use
!includedirectives - Deprecated Syntax: Check for legacy platform syntax vs modern format
- Proper Indentation: YAML requires consistent spacing (2 spaces recommended)
- Entity Naming: Consistent, descriptive entity_id patterns
- Automation Organization: Group related automations logically
Modern vs Legacy Configuration
Modern Format (Preferred):
template:
- sensor:
- name: "My Sensor"
state: "{{ states('sensor.source') }}"
Legacy Format (Deprecated):
sensor:
- platform: template
sensors:
my_sensor:
value_template: "{{ states('sensor.source') }}"
REST API Reference
Core Endpoints
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/ |
API status check |
| GET | /api/config |
Current configuration |
| GET | /api/states |
All entity states |
| GET | /api/states/<entity_id> |
Specific entity state |
| POST | /api/states/<entity_id> |
Set entity state |
| GET | /api/services |
Available services by domain |
| POST | /api/services/<domain>/<service> |
Call a service |
| GET | /api/events |
Event types and listeners |
| POST | /api/events/<event_type> |
Fire an event |
| GET | /api/history/period/<timestamp> |
Historical states |
| GET | /api/logbook/<timestamp> |
Logbook entries |
| GET | /api/error_log |
Error log (plain text) |
| POST | /api/template |
Render a template |
| POST | /api/config/core/check_config |
Validate configuration |
| GET | /api/components |
Loaded components |
| GET | /api/calendars |
Calendar entities |
Common API Operations
Get All Entity States:
curl -s "http://HA_HOST:8123/api/states" \
-H "Authorization: Bearer TOKEN" | jq '.'
Get Specific Entity:
curl -s "http://HA_HOST:8123/api/states/light.living_room" \
-H "Authorization: Bearer TOKEN" | jq '.'
Call a Service (Turn On Light):
curl -X POST "http://HA_HOST:8123/api/services/light/turn_on" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "light.living_room", "brightness": 255}'
Call a Service (Run Script):
curl -X POST "http://HA_HOST:8123/api/services/script/turn_on" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "script.my_script"}'
Trigger Automation:
curl -X POST "http://HA_HOST:8123/api/services/automation/trigger" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "automation.my_automation"}'
Render Template:
curl -X POST "http://HA_HOST:8123/api/template" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"template": "{{ states(\"sensor.temperature\") }}"}'
Check Configuration:
curl -X POST "http://HA_HOST:8123/api/config/core/check_config" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
Get History:
curl -s "http://HA_HOST:8123/api/history/period/2024-01-01T00:00:00?filter_entity_id=sensor.temperature&end_time=2024-01-02T00:00:00" \
-H "Authorization: Bearer TOKEN" | jq '.'
Automation Management
Automation YAML Structure
automation:
- id: 'unique_automation_id'
alias: "Descriptive Automation Name"
description: "What this automation does"
mode: single # single, restart, queued, parallel
trigger:
- platform: state
entity_id: binary_sensor.motion
to: 'on'
- platform: time
at: '07:00:00'
- platform: sun
event: sunset
offset: '-00:30:00'
condition:
- condition: state
entity_id: input_boolean.automation_enabled
state: 'on'
- condition: time
after: '06:00:00'
before: '23:00:00'
action:
- service: light.turn_on
target:
entity_id: light.living_room
data:
brightness_pct: 100
- delay:
seconds: 5
- service: notify.mobile_app
data:
message: "Motion detected!"
Common Trigger Platforms
| Platform | Use Case | Example |
|---|---|---|
state |
Entity state changes | to: 'on', from: 'off' |
numeric_state |
Numeric thresholds | above: 25, below: 10 |
time |
Specific time | at: '07:00:00' |
time_pattern |
Recurring pattern | minutes: '/5' (every 5 min) |
sun |
Sunrise/sunset | event: sunset, offset: '-00:30:00' |
zone |
Location zones | entity_id: person.john, zone: zone.home, event: enter |
device |
Device triggers | Device-specific events |
webhook |
External webhooks | webhook_id: 'my_webhook' |
event |
HA events | event_type: 'my_event' |
mqtt |
MQTT messages | topic: 'home/sensor' |
template |
Template evaluation | value_template: "{{ condition }}" |
Automation Debugging
Via API - Get automation trace:
curl -s "http://HA_HOST:8123/api/states/automation.my_automation" \
-H "Authorization: Bearer TOKEN" | jq '.attributes'
Enable debug logging:
# In configuration.yaml
logger:
default: info
logs:
homeassistant.components.automation: debug
Automation Trace (UI):
- Navigate to Settings > Automations & Scenes
- Click the automation > Click "Traces" (top right)
- View step-by-step execution history
Entity Management
Entity Domains
| Domain | Description | Example |
|---|---|---|
light |
Lighting control | light.living_room |
switch |
On/off switches | switch.garage_door |
sensor |
Sensor readings | sensor.temperature |
binary_sensor |
Boolean sensors | binary_sensor.motion |
climate |
HVAC/thermostats | climate.nest |
cover |
Blinds/doors/gates | cover.garage |
fan |
Fan control | fan.bedroom |
media_player |
Media devices | media_player.tv |
camera |
Camera feeds | camera.front_door |
person |
Person tracking | person.john |
device_tracker |
Device location | device_tracker.phone |
input_boolean |
Virtual toggles | input_boolean.guest_mode |
input_number |
Virtual numbers | input_number.brightness |
input_select |
Dropdown options | input_select.mode |
input_text |
Text inputs | input_text.message |
input_datetime |
Date/time inputs | input_datetime.alarm |
group |
Entity groups | group.all_lights |
scene |
Preset states | scene.movie_time |
script |
Executable scripts | script.morning_routine |
automation |
Automations | automation.sunrise_lights |
Template Sensors
template:
- sensor:
- name: "Average Temperature"
unit_of_measurement: "°F"
state: >
{{ ((states('sensor.living_room_temp') | float) +
(states('sensor.bedroom_temp') | float)) / 2 | round(1) }}
availability: >
{{ states('sensor.living_room_temp') not in ['unknown', 'unavailable'] and
states('sensor.bedroom_temp') not in ['unknown', 'unavailable'] }}
- binary_sensor:
- name: "Anyone Home"
state: >
{{ is_state('person.john', 'home') or
is_state('person.jane', 'home') }}
device_class: presence
Helper Entities (Input Helpers)
# input_boolean for manual toggles
input_boolean:
vacation_mode:
name: Vacation Mode
icon: mdi:airplane
# input_number for adjustable values
input_number:
notification_volume:
name: Notification Volume
min: 0
max: 100
step: 5
unit_of_measurement: "%"
# input_select for mode selection
input_select:
home_mode:
name: Home Mode
options:
- Home
- Away
- Night
- Guest
Security Auditing
Security Checklist
Run these checks when auditing a Home Assistant installation:
- Secrets File Usage:
# Find hardcoded credentials (should use !secret)
grep -rE "(password|api_key|token|secret):" /config/*.yaml | grep -v "!secret"
- secrets.yaml Protection:
# Verify secrets.yaml exists and has restricted permissions
ls -la /config/secrets.yaml
# Should be: -rw------- (600) or -rw-r----- (640)
- Exposed Ports:
# Check what ports HA is listening on
netstat -tulpn | grep -E "(8123|8124)"
- Authentication Review:
# Check auth configuration
grep -A 10 "homeassistant:" /config/configuration.yaml
# Look for: auth_providers, trusted_networks, ip_ban_enabled
- Add-on Security:
# Review add-on configurations for:
# - Unnecessary privileged access
# - Exposed ports
# - Disabled SSL
Security Best Practices
# configuration.yaml security settings
homeassistant:
auth_providers:
- type: homeassistant
# Uncomment for trusted networks (use carefully)
# auth_providers:
# - type: trusted_networks
# trusted_networks:
# - 192.168.1.0/24
http:
# Enable SSL (recommended)
ssl_certificate: /ssl/fullchain.pem
ssl_key: /ssl/privkey.pem
# IP banning for failed logins
ip_ban_enabled: true
login_attempts_threshold: 5
# Restrict to local only (if not using remote access)
# server_host: 127.0.0.1
# Enable recorder purging to manage database size
recorder:
purge_keep_days: 10
commit_interval: 1
secrets.yaml Structure
# secrets.yaml - NEVER commit this file to git
# Add to .gitignore: secrets.yaml
# API Keys
openweathermap_api_key: "your-api-key-here"
google_api_key: "your-google-key"
# Passwords
mqtt_password: "secure-password"
influxdb_password: "another-password"
# Tokens
telegram_bot_token: "bot123456:ABC-DEF..."
pushover_api_token: "your-token"
# URLs with credentials
database_url: "mysql://user:pass@localhost/hass"
# GPS coordinates (privacy)
home_latitude: 40.7128
home_longitude: -74.0060
Troubleshooting
Log Analysis
View Current Logs:
# Via filesystem
tail -f /config/home-assistant.log
# Via API
curl -s "http://HA_HOST:8123/api/error_log" \
-H "Authorization: Bearer TOKEN"
Enable Debug Logging:
# configuration.yaml
logger:
default: warning
logs:
homeassistant.core: debug
homeassistant.components.automation: debug
homeassistant.components.script: debug
custom_components.my_integration: debug
Filter Logs for Specific Component:
grep "homeassistant.components.sensor" /config/home-assistant.log
Common Issues & Solutions
| Issue | Diagnostic | Solution |
|---|---|---|
| Entity unavailable | Check integration status | Restart integration, check device connectivity |
| Automation not triggering | Check automation trace | Verify trigger conditions, check if enabled |
| YAML syntax error | Run config check | Fix indentation, quote strings properly |
| Integration not loading | Check logs for errors | Verify credentials, check network |
| Database errors | Check disk space | Purge old data, increase disk |
| Slow dashboard | Check entity count | Reduce entities per view, optimize templates |
| Service call fails | Check service parameters | Use Developer Tools > Services to test |
Configuration Validation
# Check configuration via CLI (HA OS)
ha core check
# Check configuration via API
curl -X POST "http://HA_HOST:8123/api/config/core/check_config" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json"
# Response on success:
# {"result": "valid", "errors": null}
# Response on error:
# {"result": "invalid", "errors": "Error details..."}
Developer Tools
Access via HA UI: Developer Tools section
| Tool | Purpose |
|---|---|
| States | View/modify entity states |
| Services | Test service calls |
| Template | Test Jinja2 templates |
| Events | Fire/listen to events |
| Statistics | View long-term statistics |
| Actions | Test action sequences |
Zigbee Integration (ZHA & Zigbee2MQTT)
Coordinator Hardware Selection
| Coordinator | Chip | Protocol | Recommendation |
|---|---|---|---|
| Home Assistant Connect ZBT-2 | EFR32MG21 | EZSP | Best - Native HA support |
| Home Assistant Yellow | EFR32MG21 | EZSP | Excellent - Built-in |
| SONOFF ZBDongle-E | EFR32MG21 | EZSP | Great - Affordable |
| SONOFF ZBDongle-P | CC2652P | Z-Stack | Great - Popular |
| SMLIGHT SLZB-07 | EFR32MG21 | EZSP | Great - Ethernet option |
| ConBee III | deCONZ | deCONZ | Good - Requires deCONZ |
| CC2531 | CC2531 | Z-Stack | Not recommended - Legacy |
USB Placement Critical:
- Use USB 2.0 ports only (USB 3.x causes RF interference)
- Use USB extension cable (1-2m) to distance from computer
- Keep away from WiFi routers, power supplies, SSDs
ZHA (Zigbee Home Automation) Setup
Initial Configuration:
- Settings > Devices & Services > Add Integration > ZHA
- Select serial port (use
/dev/serial/by-id/path for stability) - Allow network formation (creates PAN ID, channel)
YAML Configuration Options:
# configuration.yaml
zha:
zigpy_config:
network:
channel: 15 # Default, don't change unless necessary
channels: [15, 20, 25] # Scan channels
ota:
otau_directory: /config/zigpy_ota
ikea_provider: true
ledvance_provider: true
inovelli_provider: true
device_config:
# Override device type if misdetected
"aa:bb:cc:dd:ee:ff:00:11-1":
type: "switch"
custom_quirks_path: /config/custom_zha_quirks/
Device Pairing:
# Via UI: Settings > Devices & Services > ZHA > Add Device
# Put device in pairing mode (usually hold button 5-10 sec)
# Via service call (API):
curl -X POST "http://HA_HOST:8123/api/services/zha/permit" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"duration": 60}'
Network Visualization:
- Settings > Devices & Services > ZHA > Configure > Visualization
- Shows coordinator, routers, and end devices
- Color coding: Green (good), Yellow (weak), Red (poor)
ZHA Toolkit (Advanced Operations):
# Install via HACS: zha-toolkit
# Available services:
service: zha_toolkit.execute
data:
command: scan_device
ieee: "aa:bb:cc:dd:ee:ff:00:11"
# Bind devices directly (bypasses HA for faster response)
service: zha_toolkit.bind_ieee
data:
source_ieee: "aa:bb:cc:dd:ee:ff:00:11"
target_ieee: "11:22:33:44:55:66:77:88"
cluster: 6 # On/Off cluster
Zigbee2MQTT Setup
Prerequisites:
- MQTT broker (Mosquitto addon)
- Zigbee coordinator
Installation:
- Settings > Add-ons > Add-on Store
- Add repository:
https://github.com/zigbee2mqtt/hassio-zigbee2mqtt - Install Zigbee2MQTT addon
- Configure serial port and MQTT
Configuration (addon config):
serial:
port: /dev/serial/by-id/usb-Silicon_Labs_...
adapter: ezsp # or zstack for CC2652
mqtt:
server: mqtt://core-mosquitto:1883
user: mqtt_user
password: mqtt_pass
homeassistant: true
permit_join: false
frontend:
port: 8080
advanced:
channel: 15
network_key: GENERATE
pan_id: GENERATE
log_level: info
last_seen: ISO_8601
Device Pairing in Z2M:
- Open Zigbee2MQTT dashboard (sidebar)
- Click "Permit Join (All)" or specific device
- Put device in pairing mode
- Device appears automatically
Z2M vs ZHA Comparison:
| Feature | ZHA | Zigbee2MQTT |
|---|---|---|
| Setup | Easier (built-in) | More steps |
| Device Support | Good | Excellent (more quirks) |
| Configuration | Limited YAML | Full YAML control |
| Frontend | Basic | Rich dashboard |
| MQTT Required | No | Yes |
| Updates | With HA | Independent |
Zigbee Network Optimization
Best Practices:
- Router Devices: Add mains-powered devices first (they extend mesh)
- Channel Selection: Stay on channel 15 (default), avoid WiFi overlap
- Coordinator Placement: Central location, elevated
- Max Direct Children: ~32 per coordinator, but hundreds via routers
Interference Mitigation:
# Check WiFi channel overlap
# Zigbee 11 = WiFi 1
# Zigbee 15 = WiFi 1-6 (some overlap)
# Zigbee 20 = WiFi 6-8
# Zigbee 25 = WiFi 11-13
# Zigbee 26 = WiFi 12-14
OTA Firmware Updates:
# ZHA OTA - automatic for supported devices
# Wake battery devices to receive updates
# IKEA, Inovelli, Ledvance supported
# Check update status in device info
# Updates take ~10 minutes per device
Zigbee Troubleshooting
| Issue | Diagnosis | Solution |
|---|---|---|
| Device won't pair | Too far from coordinator | Pair near coordinator, move after |
| Device drops offline | Weak signal/no router nearby | Add router device in between |
| Commands delayed | Network congestion | Add more routers, check interference |
| NCP failed state | Serial communication lost | Check USB connection, restart addon |
| Entity unavailable | Device interview incomplete | Remove and re-pair device |
Debug Logging:
# configuration.yaml
logger:
default: warning
logs:
homeassistant.components.zha: debug
zigpy: debug
bellows: debug
zhaquirks: debug
Z-Wave Integration (Z-Wave JS)
Z-Wave Controller Hardware
| Controller | Chip | Generation | Notes |
|---|---|---|---|
| Zooz ZST39 | 800LR | Gen 8 | Best - Long Range support |
| Aeotec Z-Stick 7 | 700 | Gen 7 | Excellent |
| Zooz ZST10 | 700 | Gen 7 | Great value |
| HUSBZB-1 | 500 | Gen 5 | Combined Zigbee (legacy) |
USB Placement:
- Same as Zigbee: USB 2.0, extension cable, away from interference
Z-Wave JS Setup
Installation:
- Settings > Add-ons > Z-Wave JS (official addon)
- Configure serial port
- Start addon
- Settings > Devices & Services > Add Integration > Z-Wave
Security Keys (Auto-generated):
# Keys stored in addon configuration
# S0 Legacy - older secure devices (locks, garage doors)
# S2 Unauthenticated - devices that don't verify user
# S2 Authenticated - devices requiring PIN entry
# S2 Access Control - door locks, secure access
# View keys: Settings > Add-ons > Z-Wave JS > Configuration
Device Inclusion:
# SmartStart (Preferred for S2 devices):
# 1. Scan QR code from device
# 2. Device auto-joins when powered
# Classic Inclusion:
# 1. Settings > Devices & Services > Z-Wave > Add Device
# 2. Put device in inclusion mode
# 3. Enter PIN if prompted (for S2)
# Exclusion (remove from old network):
# 1. Click "Remove Device" in Z-Wave panel
# 2. Put device in exclusion mode
Z-Wave JS UI (Advanced)
Install Z-Wave JS UI addon for:
- Full network visualization
- Device parameter configuration
- Firmware updates
- Advanced diagnostics
Configuration:
# Z-Wave JS UI addon config
serial:
port: /dev/serial/by-id/usb-...
network_key: # Auto-generated or from old network
s0_legacy: # S0 key
s2_unauthenticated: # S2 key
s2_authenticated: # S2 key
s2_access_control: # S2 key
Z-Wave Network Management
Network Healing:
# Rebuild routes after moving devices
# Settings > Devices & Services > Z-Wave > Heal Network
# Or via service:
curl -X POST "http://HA_HOST:8123/api/services/zwave_js/heal_network" \
-H "Authorization: Bearer TOKEN"
Device Interview:
# Re-interview device to refresh capabilities
service: zwave_js.refresh_value
data:
entity_id: switch.device_name
# Or refresh entire device
service: zwave_js.refresh_node_info
data:
device_id: device_id_here
Z-Wave Troubleshooting
| Issue | Diagnosis | Solution |
|---|---|---|
| Device shows "Dead" | Communication lost | Check range, heal network |
| No entities | Interview incomplete | Wait or re-interview |
| S2 pairing fails | Wrong PIN | Check device documentation |
| Intermittent control | Weak mesh | Add more nodes, heal network |
| USB not detected | Driver issue | Check system logs, try USB 2.0 hub |
Debug Logging:
logger:
logs:
homeassistant.components.zwave_js: debug
zwave_js_server: debug
Thread & Matter Integration
Thread Network Setup
Thread Border Router Options:
- Home Assistant Yellow - Built-in Thread radio
- Home Assistant Connect ZBT-1/ZBT-2 - USB Thread adapter
- Apple HomePod Mini - Apple ecosystem
- Google Nest Hub (2nd gen) - Google ecosystem
OpenThread Border Router Addon:
# Install addon: OpenThread Border Router
# Configure for Thread radio
# Settings > Devices & Services > Thread
# View network credentials, preferred network
Thread Credential Sharing:
# Share HA Thread network to phone:
# Settings > Devices & Services > Thread > Configure
# "Send credentials to phone"
# Import existing Thread network:
# Use Companion App > Settings > Troubleshooting
# "Sync Thread credentials"
Matter Integration
Prerequisites:
- Home Assistant 2023.1+
- Matter Server addon (auto-installed with integration)
- Companion App (for commissioning)
- Thread border router (for Thread devices)
Setup:
- Settings > Devices & Services > Add Integration > Matter
- Matter Server addon starts automatically
Device Commissioning:
# iOS:
# 1. HA App > Settings > Devices & Services
# 2. Add Matter device > "No, it's new"
# 3. Scan QR code or enter setup code
# Android:
# 1. Same steps as iOS
# 2. May need to add device to Google Developer Console for test devices
# Commissioning can take several minutes
Multi-Admin (Multi-Fabric):
# Matter devices support up to 5 controllers
# Share from Apple Home or Google Home to HA:
# 1. In Apple/Google app, get sharing QR code
# 2. In HA, add Matter device using that code
# No factory reset needed!
Matter Device Types Supported:
- Lights (on/off, dimming, color)
- Switches and plugs
- Sensors (temperature, humidity, motion, contact)
- Locks
- Thermostats
- Window coverings
- Bridges (Philips Hue, etc.)
Thread/Matter Troubleshooting
| Issue | Solution |
|---|---|
| "Matter is unavailable" | Wait 24h for Google Play Services update, update Companion App |
| Commissioning fails | Ensure phone on same WiFi network as HA |
| Thread device won't join | Verify border router active and nearby |
| Device shows offline | Check Thread network in Thread integration |
| Can't share to phone | Sync Thread credentials via Companion App |
Debug Logging:
logger:
logs:
homeassistant.components.matter: debug
homeassistant.components.thread: debug
matter_server: debug
ESPHome Device Builder
ESPHome Fundamentals
Installation:
- Settings > Add-ons > ESPHome (official addon)
- Open ESPHome dashboard from sidebar
Supported Hardware:
- ESP8266 (NodeMCU, Wemos D1, Sonoff)
- ESP32 (DevKit, WROOM, various modules)
- ESP32-S2, ESP32-S3, ESP32-C3
- RP2040 (Raspberry Pi Pico W)
YAML Configuration Structure
Basic Device Template:
esphome:
name: living-room-sensor
friendly_name: Living Room Sensor
esp32:
board: esp32dev
framework:
type: arduino # or esp-idf
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
# Fallback hotspot
ap:
ssid: "Sensor Fallback"
password: "fallback123"
captive_portal:
# Enable logging
logger:
level: DEBUG
# Enable Home Assistant API
api:
encryption:
key: !secret api_encryption_key
ota:
- platform: esphome
password: !secret ota_password
# Optional web server
web_server:
port: 80
Common Sensor Configurations
DHT Temperature/Humidity:
sensor:
- platform: dht
pin: GPIO4
model: DHT22 # or DHT11, AM2302
temperature:
name: "Temperature"
filters:
- offset: -2.0 # Calibration offset
humidity:
name: "Humidity"
update_interval: 60s
BME280 (I2C Temperature/Humidity/Pressure):
i2c:
sda: GPIO21
scl: GPIO22
scan: true
sensor:
- platform: bme280_i2c
address: 0x76 # or 0x77
temperature:
name: "Temperature"
oversampling: 16x
humidity:
name: "Humidity"
pressure:
name: "Pressure"
update_interval: 60s
PIR Motion Sensor:
binary_sensor:
- platform: gpio
pin: GPIO14
name: "Motion"
device_class: motion
filters:
- delayed_off: 30s # Stay on for 30s after motion stops
Door/Window Contact:
binary_sensor:
- platform: gpio
pin:
number: GPIO5
mode:
input: true
pullup: true
inverted: true
name: "Door"
device_class: door
Analog Sensor (Moisture/Light):
sensor:
- platform: adc
pin: GPIO34
name: "Soil Moisture"
update_interval: 60s
unit_of_measurement: "%"
filters:
- calibrate_linear:
- 3.3 -> 0.0 # Dry
- 1.5 -> 100.0 # Wet
attenuation: 11db
Ultrasonic Distance:
sensor:
- platform: ultrasonic
trigger_pin: GPIO12
echo_pin: GPIO14
name: "Tank Level"
update_interval: 60s
filters:
- lambda: return (1.0 - x) * 100; # Convert to percentage
unit_of_measurement: "%"
Output Configurations
GPIO Switch (Relay):
switch:
- platform: gpio
pin: GPIO5
name: "Relay"
id: relay1
restore_mode: RESTORE_DEFAULT_OFF
PWM LED/Dimmer:
output:
- platform: ledc
pin: GPIO16
id: pwm_output
frequency: 1000Hz
light:
- platform: monochromatic
name: "LED"
output: pwm_output
gamma_correct: 2.8
RGB LED Strip (Addressable):
light:
- platform: neopixelbus
type: GRB
variant: WS2812
pin: GPIO5
num_leds: 60
name: "LED Strip"
effects:
- random:
- rainbow:
- color_wipe:
- scan:
Servo Motor:
servo:
- id: my_servo
output: pwm_servo
output:
- platform: ledc
id: pwm_servo
pin: GPIO18
frequency: 50Hz
# Control via number component
number:
- platform: template
name: "Servo Position"
min_value: -100
max_value: 100
step: 1
set_action:
- servo.write:
id: my_servo
level: !lambda 'return x / 100.0;'
Communication Protocols
I2C Bus:
i2c:
sda: GPIO21
scl: GPIO22
scan: true
id: bus_a
frequency: 400kHz
SPI Bus:
spi:
clk_pin: GPIO18
mosi_pin: GPIO23
miso_pin: GPIO19
UART (Serial):
uart:
tx_pin: GPIO1
rx_pin: GPIO3
baud_rate: 9600
id: uart_bus
Dallas 1-Wire (DS18B20):
dallas:
- pin: GPIO4
sensor:
- platform: dallas
address: 0x1234567890ABCDEF
name: "Temperature"
resolution: 12
Advanced ESPHome Features
Lambda Expressions:
sensor:
- platform: template
name: "Calculated Value"
lambda: |-
float temp = id(temperature_sensor).state;
float hum = id(humidity_sensor).state;
// Calculate heat index
return temp + (0.5 * (temp + 61.0 + ((temp-68.0)*1.2) + (hum*0.094)));
update_interval: 60s
unit_of_measurement: "°F"
Automation (On-Device):
binary_sensor:
- platform: gpio
pin: GPIO14
name: "Motion"
on_press:
- light.turn_on:
id: led_light
brightness: 100%
- delay: 5min
- light.turn_off: led_light
Time-Based Actions:
time:
- platform: homeassistant
id: ha_time
on_time:
- seconds: 0
minutes: 0
hours: 7
then:
- switch.turn_on: morning_light
Substitutions (Variables):
substitutions:
device_name: living-room
friendly_name: "Living Room"
update_interval: 60s
esphome:
name: ${device_name}
friendly_name: ${friendly_name}
sensor:
- platform: dht
update_interval: ${update_interval}
Packages (Reusable Configs):
# common.yaml
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
api:
encryption:
key: !secret api_key
# device.yaml
packages:
common: !include common.yaml
esphome:
name: my-device
ESPHome Flashing & Updates
Initial Flash (USB):
- Connect device via USB
- In ESPHome dashboard, click "Install"
- Select "Plug into this computer"
- Choose serial port
OTA Updates (Wireless):
ota:
- platform: esphome
password: "secure_ota_password"
safe_mode: true
# Updates happen automatically when config changes
# Or manually: Install > Wirelessly
Secrets Management:
# secrets.yaml (in ESPHome config directory)
wifi_ssid: "MyNetwork"
wifi_password: "MyPassword"
api_encryption_key: "base64_encoded_32_byte_key"
ota_password: "ota_secure_pass"
# Usage in device config
wifi:
ssid: !secret wifi_ssid
password: !secret wifi_password
ESPHome Troubleshooting
| Issue | Solution |
|---|---|
| Won't connect to WiFi | Check credentials, signal strength, 2.4GHz only |
| API connection fails | Check encryption key, firewall |
| Sensor reads incorrectly | Add calibration filters |
| Device reboots randomly | Check power supply (3.3V devices need stable power) |
| OTA update fails | Ensure enough flash space, try safe mode |
| Compile error | Check YAML syntax, pin conflicts |
Debug Output:
logger:
level: DEBUG # VERBOSE for even more
logs:
sensor: DEBUG
wifi: INFO
api: DEBUG
Advanced Troubleshooting
Log Analysis Deep Dive
Log Locations:
# Home Assistant OS
/config/home-assistant.log # Current log
/config/home-assistant.log.1 # Previous log
# Docker
docker logs homeassistant
# Core (venv)
~/.homeassistant/home-assistant.log
Log Levels:
# configuration.yaml
logger:
default: warning # critical, fatal, error, warning, info, debug
logs:
# Per-component logging
homeassistant.core: warning
homeassistant.components.automation: debug
homeassistant.components.script: debug
homeassistant.components.zha: info
homeassistant.components.zwave_js: info
# Third-party libraries
zigpy: debug
zwave_js_server: debug
aiohttp: warning
# Custom components
custom_components.my_integration: debug
Real-time Log Monitoring:
# Via API
curl -s "http://HA_HOST:8123/api/error_log" \
-H "Authorization: Bearer TOKEN"
# Via SSH (HA OS)
tail -f /config/home-assistant.log | grep -i error
# Via Docker
docker logs -f homeassistant 2>&1 | grep -i error
Log Filtering Patterns:
# Find all errors
grep -i "error\|exception\|traceback" home-assistant.log
# Find specific integration issues
grep "homeassistant.components.zha" home-assistant.log
# Find startup issues
grep -A 5 "Setup of" home-assistant.log | grep -i "fail\|error"
# Find automation execution
grep "automation" home-assistant.log | grep -i "triggered\|executed"
Automation Debugging
Automation Traces:
- Settings > Automations & Scenes
- Click automation > Three-dot menu > Traces
- View step-by-step execution path
- Check variable values at each step
Increase Trace Storage:
# In automation definition
automation:
- id: my_automation
alias: "My Automation"
trace:
stored_traces: 25 # Default is 5
Template Testing:
# Developer Tools > Template
# Test Jinja2 expressions before using in automations
# Example test:
{{ states('sensor.temperature') | float > 75 }}
{{ trigger.to_state.state }} # Only works in automation context
Manual Automation Trigger:
# Via API (with conditions)
curl -X POST "http://HA_HOST:8123/api/services/automation/trigger" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "automation.my_auto", "skip_condition": false}'
# Via API (skip conditions)
curl -X POST "http://HA_HOST:8123/api/services/automation/trigger" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"entity_id": "automation.my_auto", "skip_condition": true}'
Common Automation Issues:
| Issue | Diagnosis | Solution |
|---|---|---|
| Never triggers | Check trigger conditions in trace | Verify entity_id, states match |
| Triggers too often | Review trigger type | Add conditions or throttle |
| Actions don't execute | Check condition block in trace | Fix condition logic |
| Template error | Test in Developer Tools | Fix Jinja2 syntax |
| Wrong entity controlled | entity_id mismatch | Check entity registry |
| Variables not available | Scope issue | Use trigger variables correctly |
Performance Diagnostics
System Metrics:
# Add system monitoring
sensor:
- platform: systemmonitor
resources:
- type: processor_use
- type: memory_use_percent
- type: disk_use_percent
arg: /
- type: load_1m
- type: load_5m
- type: network_in
arg: eth0
- type: network_out
arg: eth0
Entity Count Impact:
# Check entity count via API
curl -s "http://HA_HOST:8123/api/states" \
-H "Authorization: Bearer TOKEN" | jq 'length'
# Target: < 1000 entities for smooth performance
# > 2000 entities: Consider recorder optimization
Startup Time Analysis:
# Enable startup timing
homeassistant:
debug: true
# Check logs for:
# "Setup of integration X took X.XX seconds"
Database & Recorder Optimization
Recorder Configuration
Basic Optimization:
# configuration.yaml
recorder:
purge_keep_days: 7 # Reduce from default 10
commit_interval: 5 # Seconds between writes (default 1)
# Exclude high-frequency entities
exclude:
domains:
- automation
- updater
- camera
entity_globs:
- sensor.date_*
- sensor.time_*
- sensor.uptime_*
entities:
- sensor.last_boot
- sun.sun
Aggressive Filtering:
recorder:
purge_keep_days: 5
commit_interval: 10
# Include only what you need
include:
domains:
- sensor
- binary_sensor
- switch
- light
- climate
entities:
- person.john
- person.jane
# Still exclude noisy entities
exclude:
entity_globs:
- sensor.*_linkquality
- sensor.*_battery_*
- sensor.*_signal_*
MariaDB Migration (Recommended)
Install MariaDB Addon:
- Settings > Add-ons > MariaDB
- Configure with strong password
- Start addon
Configuration:
# configuration.yaml
recorder:
db_url: mysql://homeassistant:PASSWORD@core-mariadb/homeassistant?charset=utf8mb4
purge_keep_days: 14
commit_interval: 1
MariaDB Tuning (Advanced):
# Custom mariadb options (in addon config)
innodb_buffer_pool_size = 256M
innodb_log_file_size = 64M
innodb_flush_log_at_trx_commit = 2
innodb_flush_method = O_DIRECT
PostgreSQL Migration
Docker Setup:
# docker-compose.yml addition
postgres:
image: postgres:15
environment:
POSTGRES_DB: homeassistant
POSTGRES_USER: homeassistant
POSTGRES_PASSWORD: secure_password
volumes:
- ./postgres_data:/var/lib/postgresql/data
Configuration:
recorder:
db_url: postgresql://homeassistant:secure_password@postgres/homeassistant
Long-Term Statistics (InfluxDB)
Use Case: Keep detailed history for years without database bloat
InfluxDB Addon Setup:
- Install InfluxDB addon
- Create database and user
- Configure integration
Configuration:
# configuration.yaml
influxdb:
host: a0d7b954-influxdb
port: 8086
database: homeassistant
username: homeassistant
password: !secret influxdb_password
max_retries: 3
# Only send important sensors
include:
domains:
- sensor
entities:
- climate.thermostat
# Exclude noisy data
exclude:
entity_globs:
- sensor.*_battery
Database Maintenance
Manual Purge:
# Via service call
curl -X POST "http://HA_HOST:8123/api/services/recorder/purge" \
-H "Authorization: Bearer TOKEN" \
-H "Content-Type: application/json" \
-d '{"keep_days": 5, "repack": true}'
Statistics Cleanup:
# Remove old statistics
service: recorder.purge_entities
data:
entity_id:
- sensor.old_entity
keep_days: 0
Database Size Check (SQLite):
# Via SSH
ls -lh /config/home-assistant_v2.db
# Should be < 1GB for smooth operation
# If larger, increase filtering or migrate to MariaDB
Security Hardening
SSL/TLS Configuration
Let's Encrypt with DuckDNS:
# Install DuckDNS addon
# Configure with your subdomain and token
# configuration.yaml
http:
ssl_certificate: /ssl/fullchain.pem
ssl_key: /ssl/privkey.pem
server_port: 443
Self-Signed Certificate:
# Generate certificate
openssl req -x509 -newkey rsa:4096 \
-keyout /ssl/privkey.pem \
-out /ssl/fullchain.pem \
-days 365 -nodes \
-subj "/CN=homeassistant.local"
# configuration.yaml
http:
ssl_certificate: /ssl/fullchain.pem
ssl_key: /ssl/privkey.pem
Nginx Proxy Manager:
# Use NPM addon for SSL termination
# Supports Let's Encrypt auto-renewal
# Reverse proxy to HA on port 8123
Authentication Hardening
IP Banning:
http:
ip_ban_enabled: true
login_attempts_threshold: 5
# Banned IPs stored in /config/ip_bans.yaml
Trusted Networks:
homeassistant:
auth_providers:
- type: homeassistant
- type: trusted_networks
trusted_networks:
- 192.168.1.0/24
- 10.0.0.0/8
trusted_users:
192.168.1.100:
- user_id_here
allow_bypass_login: true # Skip login from trusted networks
Multi-Factor Authentication:
- Profile > Security > Multi-factor auth modules
- Enable TOTP (Time-based One-Time Password)
- Scan QR code with authenticator app
Network Security
Firewall Rules (Linux):
# Allow only local network to HA
sudo ufw allow from 192.168.1.0/24 to any port 8123
# Block external access
sudo ufw deny 8123
Reverse Proxy Headers:
http:
use_x_forwarded_for: true
trusted_proxies:
- 192.168.1.1 # Your proxy IP
- 172.30.33.0/24 # Docker network
Secrets Management Best Practices
secrets.yaml Structure:
# /config/secrets.yaml
# CRITICAL: Add to .gitignore
# API Keys
openweathermap_api: "abc123..."
google_api_key: "xyz789..."
# Passwords
mqtt_password: "secure_mqtt_pass"
mariadb_password: "secure_db_pass"
influxdb_password: "secure_influx_pass"
# Tokens
telegram_bot_token: "bot123:ABC..."
pushover_api_key: "po_key..."
# Sensitive URLs
database_url: "mysql://user:pass@host/db"
# Coordinates (privacy)
home_latitude: 40.7128
home_longitude: -74.0060
home_elevation: 10
# Encryption keys
api_encryption_key: "base64_32_byte_key..."
Environment Variables (Docker):
# docker-compose.yml
environment:
- HASS_HTTP_SSL_CERTIFICATE=/ssl/fullchain.pem
- HASS_HTTP_SSL_KEY=/ssl/privkey.pem
Security Audit Checklist
# 1. Check for hardcoded secrets
grep -rE "(password|api_key|token):" /config/*.yaml | grep -v "!secret"
# 2. Verify secrets.yaml permissions
ls -la /config/secrets.yaml # Should be 600 or 640
# 3. Check exposed ports
netstat -tlnp | grep -E "8123|1883|8080"
# 4. Review auth providers
grep -A 10 "auth_providers" /config/configuration.yaml
# 5. Check for default passwords
grep -r "password: admin\|password: homeassistant" /config/
# 6. Verify SSL is enabled
grep -A 5 "http:" /config/configuration.yaml | grep ssl
# 7. Check IP ban status
cat /config/ip_bans.yaml 2>/dev/null || echo "No bans"
Custom Integration Development
Project Structure
custom_components/
└── my_integration/
├── __init__.py # Integration setup
├── manifest.json # Integration metadata
├── config_flow.py # UI configuration
├── const.py # Constants
├── sensor.py # Sensor platform
├── switch.py # Switch platform
├── strings.json # English strings
└── translations/
└── en.json # Translations
manifest.json
{
"domain": "my_integration",
"name": "My Integration",
"version": "1.0.0",
"documentation": "https://github.com/user/my_integration",
"issue_tracker": "https://github.com/user/my_integration/issues",
"dependencies": [],
"codeowners": ["@username"],
"requirements": ["some_library==1.0.0"],
"config_flow": true,
"iot_class": "local_polling"
}
IoT Classes:
local_push- Device pushes updateslocal_polling- HA polls device locallycloud_push- Cloud pushes updatescloud_polling- HA polls cloud APIcalculated- Derived from other entities
Basic Integration (init.py)
"""My Integration."""
from homeassistant.config_entries import ConfigEntry
from homeassistant.core import HomeAssistant
DOMAIN = "my_integration"
PLATFORMS = ["sensor", "switch"]
async def async_setup_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Set up from a config entry."""
hass.data.setdefault(DOMAIN, {})
hass.data[DOMAIN][entry.entry_id] = entry.data
await hass.config_entries.async_forward_entry_setups(entry, PLATFORMS)
return True
async def async_unload_entry(hass: HomeAssistant, entry: ConfigEntry) -> bool:
"""Unload a config entry."""
unload_ok = await hass.config_entries.async_unload_platforms(entry, PLATFORMS)
if unload_ok:
hass.data[DOMAIN].pop(entry.entry_id)
return unload_ok
Config Flow (UI Setup)
"""Config flow for My Integration."""
import voluptuous as vol
from homeassistant import config_entries
from homeassistant.const import CONF_HOST, CONF_PASSWORD
from .const import DOMAIN
class MyIntegrationConfigFlow(config_entries.ConfigFlow, domain=DOMAIN):
"""Handle a config flow."""
VERSION = 1
async def async_step_user(self, user_input=None):
"""Handle the initial step."""
errors = {}
if user_input is not None:
# Validate input
try:
# Test connection
await self._test_connection(user_input[CONF_HOST])
except ConnectionError:
errors["base"] = "cannot_connect"
else:
return self.async_create_entry(
title=user_input[CONF_HOST],
data=user_input
)
return self.async_show_form(
step_id="user",
data_schema=vol.Schema({
vol.Required(CONF_HOST): str,
vol.Optional(CONF_PASSWORD): str,
}),
errors=errors,
)
async def _test_connection(self, host):
"""Test connection to device."""
# Implement connection test
pass
Sensor Platform
"""Sensor platform for My Integration."""
from homeassistant.components.sensor import (
SensorEntity,
SensorDeviceClass,
SensorStateClass,
)
from homeassistant.const import UnitOfTemperature
async def async_setup_entry(hass, entry, async_add_entities):
"""Set up sensor platform."""
async_add_entities([MySensor(entry)])
class MySensor(SensorEntity):
"""My sensor entity."""
_attr_device_class = SensorDeviceClass.TEMPERATURE
_attr_state_class = SensorStateClass.MEASUREMENT
_attr_native_unit_of_measurement = UnitOfTemperature.CELSIUS
def __init__(self, entry):
"""Initialize sensor."""
self._attr_name = "My Temperature"
self._attr_unique_id = f"{entry.entry_id}_temperature"
self._attr_native_value = None
async def async_update(self):
"""Update sensor state."""
# Fetch data from device/API
self._attr_native_value = 21.5
Async Patterns
"""Async data coordinator pattern."""
from datetime import timedelta
from homeassistant.helpers.update_coordinator import DataUpdateCoordinator
class MyCoordinator(DataUpdateCoordinator):
"""Data coordinator for My Integration."""
def __init__(self, hass, client):
"""Initialize coordinator."""
super().__init__(
hass,
_LOGGER,
name="My Integration",
update_interval=timedelta(seconds=30),
)
self.client = client
async def _async_update_data(self):
"""Fetch data from API."""
try:
return await self.client.async_get_data()
except ApiError as err:
raise UpdateFailed(f"Error fetching data: {err}")
Advanced Jinja2 Templating
Template Fundamentals
Testing Templates:
- Developer Tools > Template
- Live preview as you type
- Access to all entities and states
Basic Syntax:
{# Comment - not rendered #}
{{ expression }} {# Output expression result #}
{% statement %} {# Control flow #}
State Access
{# Get entity state #}
{{ states('sensor.temperature') }}
{# Get attribute #}
{{ state_attr('sensor.temperature', 'unit_of_measurement') }}
{# Check state #}
{{ is_state('light.living_room', 'on') }}
{# Get all attributes #}
{{ states.sensor.temperature.attributes }}
{# Last changed timestamp #}
{{ states.sensor.temperature.last_changed }}
Filters & Functions
{# Type conversion #}
{{ states('sensor.temp') | float }}
{{ states('sensor.count') | int }}
{{ 'true' | bool }}
{# Math operations #}
{{ (states('sensor.temp') | float) * 1.8 + 32 }}
{{ states('sensor.value') | float | round(2) }}
{{ [1, 2, 3] | sum }}
{{ [1, 2, 3] | average }}
{# String operations #}
{{ 'hello world' | upper }}
{{ 'HELLO' | lower }}
{{ 'hello' | capitalize }}
{{ 'hello world' | title }}
{{ 'text' | replace('e', 'a') }}
{{ 'a,b,c' | split(',') }}
{# Date/time #}
{{ now() }}
{{ now().strftime('%Y-%m-%d %H:%M') }}
{{ as_timestamp(now()) }}
{{ as_datetime(states('sensor.timestamp')) }}
{{ relative_time(states.sensor.motion.last_changed) }}
{# Default values #}
{{ states('sensor.maybe_missing') | default('N/A') }}
{{ states('sensor.temp') | float(0) }} {# Default if conversion fails #}
Control Structures
{# Conditionals #}
{% if is_state('light.living_room', 'on') %}
Light is on
{% elif is_state('light.living_room', 'unavailable') %}
Light is unavailable
{% else %}
Light is off
{% endif %}
{# Ternary operator #}
{{ 'Occupied' if is_state('binary_sensor.motion', 'on') else 'Empty' }}
{# Loops #}
{% for light in states.light %}
{{ light.entity_id }}: {{ light.state }}
{% endfor %}
{# Loop with filter #}
{% for light in states.light | selectattr('state', 'eq', 'on') %}
{{ light.name }} is on
{% endfor %}
{# Loop with index #}
{% for item in ['a', 'b', 'c'] %}
{{ loop.index }}: {{ item }}
{% endfor %}
Advanced Templates
Count Entities by State:
{{ states.light | selectattr('state', 'eq', 'on') | list | count }}
{# With area filter #}
{{ states.light
| selectattr('state', 'eq', 'on')
| selectattr('attributes.area_id', 'eq', 'living_room')
| list | count }}
List All Entities in State:
{% set on_lights = states.light | selectattr('state', 'eq', 'on') | map(attribute='name') | list %}
{{ on_lights | join(', ') if on_lights else 'No lights on' }}
Time-Based Logic:
{% set hour = now().hour %}
{% if hour < 6 %}Night
{% elif hour < 12 %}Morning
{% elif hour < 18 %}Afternoon
{% else %}Evening{% endif %}
{# Is it daytime? #}
{{ is_state('sun.sun', 'above_horizon') }}
{# Minutes until sunset #}
{{ ((as_timestamp(state_attr('sun.sun', 'next_setting')) - as_timestamp(now())) / 60) | round }}
Sensor Aggregation:
{% set temps = [
states('sensor.living_room_temp') | float,
states('sensor.bedroom_temp') | float,
states('sensor.kitchen_temp') | float
] %}
Average: {{ (temps | sum / temps | length) | round(1) }}°
Max: {{ temps | max }}°
Min: {{ temps | min }}°
Custom Template Sensors
# configuration.yaml
template:
- sensor:
- name: "Lights On Count"
unique_id: lights_on_count
state: >
{{ states.light | selectattr('state', 'eq', 'on') | list | count }}
icon: mdi:lightbulb-group
- name: "Average Indoor Temperature"
unique_id: avg_indoor_temp
state: >
{% set temps = [
states('sensor.living_room_temp'),
states('sensor.bedroom_temp'),
states('sensor.office_temp')
] | map('float', 0) | select('>', 0) | list %}
{{ (temps | sum / temps | count) | round(1) if temps else 'unknown' }}
unit_of_measurement: "°F"
device_class: temperature
state_class: measurement
availability: >
{{ states('sensor.living_room_temp') not in ['unavailable', 'unknown'] }}
- binary_sensor:
- name: "Anyone Home"
unique_id: anyone_home
state: >
{{ states.person | selectattr('state', 'eq', 'home') | list | count > 0 }}
device_class: presence
- name: "Windows Open"
unique_id: windows_open
state: >
{{ states.binary_sensor
| selectattr('attributes.device_class', 'eq', 'window')
| selectattr('state', 'eq', 'on')
| list | count > 0 }}
device_class: window
Macros (Reusable Functions)
# Create in custom_templates/macros.jinja
{% macro light_status(entity) %}
{% if is_state(entity, 'on') %}
{{ state_attr(entity, 'brightness') | int(0) / 255 * 100 | round }}%
{% else %}
Off
{% endif %}
{% endmacro %}
{% macro format_duration(seconds) %}
{% set hours = (seconds // 3600) | int %}
{% set minutes = ((seconds % 3600) // 60) | int %}
{{ hours }}h {{ minutes }}m
{% endmacro %}
Using Macros:
# configuration.yaml
homeassistant:
packages: !include_dir_named packages
# In templates, import with:
# {% from 'macros.jinja' import light_status, format_duration %}
JavaScript in Button-Card
type: custom:button-card
entity: sensor.temperature
name: |
[[[
return `Temperature: ${entity.state}°F`;
]]]
icon: |
[[[
const temp = parseFloat(entity.state);
if (temp > 80) return 'mdi:thermometer-alert';
if (temp > 70) return 'mdi:thermometer';
return 'mdi:thermometer-low';
]]]
styles:
icon:
- color: |
[[[
const temp = parseFloat(entity.state);
if (temp > 80) return '#ff5722';
if (temp > 70) return '#ff9800';
if (temp > 60) return '#4caf50';
return '#2196f3';
]]]
card:
- background: |
[[[
const temp = parseFloat(entity.state);
const hue = Math.max(0, Math.min(240, 240 - (temp - 50) * 4));
return `hsl(${hue}, 70%, 20%)`;
]]]
Dashboard Pro - Design & Customization
Dashboard Design Workflow
CRITICAL: When building a new dashboard, first ask the user:
What type of dashboard are you creating?
1. **Wall-mounted tablet** (10-15" fixed display)
- Larger touch targets, always-visible info
- Optimized for landscape orientation
- Pop-ups for detailed controls
2. **Mobile phone** (handheld, responsive)
- Compact cards, vertical scrolling
- Bottom navigation for easy thumb access
- Quick glance information
3. **Desktop/laptop browser** (large screen)
- Multi-column layouts, more data density
- Side-by-side comparisons
- Advanced graphs and charts
4. **All devices** (fully responsive)
- Uses layout-card with media queries
- Adapts to screen size automatically
HACS Setup (Required for Custom Cards)
Install HACS:
# Via SSH to Home Assistant
wget -O - https://get.hacs.xyz | bash -
# Or manually download to /config/custom_components/hacs/
Enable in configuration.yaml:
# Not required for newer HACS versions, but ensure custom resources work
frontend:
extra_module_url:
- /hacsfiles/lovelace-card-mod/card-mod.js
Essential HACS Frontend Resources:
| Card | Purpose | Install Priority |
|---|---|---|
card-mod |
CSS styling for ANY card | Required |
mushroom |
Clean, modern card collection | Required |
bubble-card |
Pop-ups, buttons, separators | Required |
button-card |
Ultimate customizable buttons | Required |
layout-card |
Responsive grid layouts | Required |
apexcharts-card |
Advanced data visualization | High |
mini-graph-card |
Simple, clean graphs | High |
stack-in-card |
Remove card borders in stacks | High |
auto-entities |
Dynamic entity lists | Medium |
browser-mod |
Browser control, pop-ups | Medium |
Theme Setup (Dark/Modern Style)
Enable themes in configuration.yaml:
frontend:
themes: !include_dir_merge_named themes
extra_module_url:
- /hacsfiles/lovelace-card-mod/card-mod.js
Recommended Dark Themes (via HACS):
| Theme | Style | Best For |
|---|---|---|
| Noctis | Dark blue, clean | Wall tablets, general use |
| Waves | Noctis + Caule blend | Modern dark aesthetic |
| Bubble | Minimalist dark | Mobile-first dashboards |
| Mushroom | Soft dark | Mushroom card pairing |
| Caule Black | True black AMOLED | Battery saving on OLED |
| iOS Dark | Apple-style | iOS users |
Install Noctis Theme:
- HACS > Frontend > Search "Noctis"
- Install and restart HA
- Profile > Theme > Select "Noctis"
Theme with card-mod enhancements:
# themes/noctis.yaml (add card-mod styling)
noctis:
# Base colors
primary-color: "#5294E2"
accent-color: "#5294E2"
# Card styling via card-mod
card-mod-theme: noctis
card-mod-card-yaml: |
.: |
ha-card {
border-radius: 12px;
box-shadow: none;
border: 1px solid rgba(255,255,255,0.1);
}
# Blur effect on more-info dialogs
card-mod-more-info-yaml: |
$: |
.mdc-dialog .mdc-dialog__scrim {
backdrop-filter: blur(15px);
-webkit-backdrop-filter: blur(15px);
background: rgba(0,0,0,.6);
}
Mushroom Cards (Foundation for Clean UIs)
Available Card Types:
| Card | Use Case |
|---|---|
mushroom-entity-card |
General entity display |
mushroom-light-card |
Light with brightness slider |
mushroom-switch-card |
Simple toggle |
mushroom-fan-card |
Fan with speed control |
mushroom-cover-card |
Blinds/covers with position |
mushroom-climate-card |
Thermostat control |
mushroom-media-player-card |
Media controls |
mushroom-person-card |
Person with location |
mushroom-alarm-control-panel-card |
Security panel |
mushroom-template-card |
Fully customizable |
mushroom-chips-card |
Status chips row |
mushroom-title-card |
Section headers |
Basic Mushroom Light Card:
type: custom:mushroom-light-card
entity: light.living_room
name: Living Room
icon: mdi:ceiling-light
use_light_color: true
show_brightness_control: true
show_color_control: true
collapsible_controls: true
Mushroom Chips Card (Status Row):
type: custom:mushroom-chips-card
chips:
- type: menu
- type: weather
entity: weather.home
show_conditions: true
show_temperature: true
- type: entity
entity: person.john
icon: mdi:face-man
- type: entity
entity: alarm_control_panel.home
- type: conditional
conditions:
- entity: binary_sensor.front_door
state: "on"
chip:
type: template
icon: mdi:door-open
icon_color: red
content: Door Open!
Mushroom Template Card (Advanced):
type: custom:mushroom-template-card
entity: sensor.living_room_temperature
primary: Living Room
secondary: "{{ states(entity) }}°F"
icon: mdi:thermometer
icon_color: |-
{% set temp = states(entity) | float %}
{% if temp > 75 %}red
{% elif temp > 70 %}orange
{% elif temp < 65 %}blue
{% else %}green{% endif %}
tap_action:
action: more-info
card_mod:
style: |
ha-card {
background: linear-gradient(135deg, rgba(82,148,226,0.2), transparent);
}
Bubble Card (Pop-ups & Modern Buttons)
Card Types:
pop-up- Full-screen overlay for detailed controlsbutton- Versatile button with sub-buttonsseparator- Visual dividershorizontal-buttons-stack- Row of buttonscover- Cover/blind controlsmedia-player- Media controlsempty-column- Spacing
Pop-up Setup:
# Step 1: Create pop-up card (place at TOP of view)
type: custom:bubble-card
card_type: pop-up
hash: '#living-room'
name: Living Room
icon: mdi:sofa
bg_color: var(--primary-color)
bg_opacity: 0.8
# Optional: blur background
styles: |
.bubble-pop-up-container {
backdrop-filter: blur(10px);
}
# Step 2: Add content cards inside the pop-up
cards:
- type: custom:mushroom-light-card
entity: light.living_room
- type: custom:mushroom-climate-card
entity: climate.living_room
Button with Sub-buttons:
type: custom:bubble-card
card_type: button
button_type: state
entity: light.living_room
name: Living Room
icon: mdi:sofa
show_state: true
card_layout: large
sub_button:
- name: Ceiling
icon: mdi:ceiling-light
entity: light.ceiling
show_state: true
tap_action:
action: toggle
- name: Lamp
icon: mdi:lamp
entity: light.lamp
show_state: true
tap_action:
action: toggle
- name: LED Strip
icon: mdi:led-strip
entity: light.led_strip
show_background: false
tap_action:
action: toggle
Navigation Button to Pop-up:
type: custom:bubble-card
card_type: button
button_type: name
name: Living Room
icon: mdi:sofa
tap_action:
action: navigate
navigation_path: '#living-room'
styles: |
.bubble-icon-container {
background: linear-gradient(135deg, #667eea, #764ba2) !important;
}
Horizontal Button Stack:
type: custom:bubble-card
card_type: horizontal-buttons-stack
buttons:
- name: Living
icon: mdi:sofa
tap_action:
action: navigate
navigation_path: '#living-room'
- name: Kitchen
icon: mdi:silverware-fork-knife
tap_action:
action: navigate
navigation_path: '#kitchen'
- name: Bedroom
icon: mdi:bed
tap_action:
action: navigate
navigation_path: '#bedroom'
Button-Card (Ultimate Customization)
Basic Button:
type: custom:button-card
entity: light.living_room
name: Living Room
icon: mdi:ceiling-light
show_state: true
tap_action:
action: toggle
styles:
card:
- border-radius: 12px
- background-color: var(--card-background-color)
icon:
- color: var(--primary-color)
Button with State-based Styling:
type: custom:button-card
entity: light.living_room
name: Living Room
icon: mdi:ceiling-light
show_state: true
color_type: icon
state:
- value: "on"
color: gold
styles:
card:
- background: linear-gradient(to bottom, rgba(255,215,0,0.3), transparent)
icon:
- animation: pulse 2s ease-in-out infinite
- value: "off"
color: gray
styles:
card:
- background: var(--card-background-color)
Button-Card Templates (Reusable Styles):
Create in ui-lovelace.yaml or button_card_templates.yaml:
button_card_templates:
# Base template for all room cards
room_card:
show_state: true
show_name: true
show_icon: true
color_type: icon
tap_action:
action: navigate
styles:
card:
- border-radius: 16px
- padding: 16px
- background: var(--card-background-color)
grid:
- grid-template-areas: '"i n" "i s"'
- grid-template-columns: 40% 1fr
- grid-template-rows: min-content min-content
icon:
- width: 50px
- color: var(--primary-color)
name:
- font-size: 16px
- font-weight: bold
- justify-self: start
state:
- font-size: 12px
- justify-self: start
- color: var(--secondary-text-color)
# Light button with animated icon when on
light_button:
template: room_card
state:
- value: "on"
styles:
icon:
- color: gold
- filter: drop-shadow(0 0 10px gold)
card:
- background: linear-gradient(135deg, rgba(255,215,0,0.2), transparent)
# Temperature sensor with color coding
temp_sensor:
show_state: true
show_name: true
show_icon: true
state_display: '[[[ return `${entity.state}°F` ]]]'
styles:
icon:
- color: |
[[[
var temp = parseFloat(entity.state);
if (temp > 80) return '#ff5722';
if (temp > 75) return '#ff9800';
if (temp > 70) return '#4caf50';
if (temp > 65) return '#03a9f4';
return '#2196f3';
]]]
Using Templates:
type: custom:button-card
template: light_button
entity: light.living_room
name: Living Room
tap_action:
action: navigate
navigation_path: '#living-room'
Card-Mod (CSS Styling for ANY Card)
Basic Styling:
type: entities
entities:
- entity: light.living_room
card_mod:
style: |
ha-card {
background: linear-gradient(135deg, #1a1a2e, #16213e);
border-radius: 16px;
border: 1px solid rgba(255,255,255,0.1);
box-shadow: 0 8px 32px rgba(0,0,0,0.3);
}
Animate Icons Based on State:
type: custom:mushroom-entity-card
entity: fan.bedroom
card_mod:
style:
mushroom-shape-icon$: |
.shape {
{% if is_state('fan.bedroom', 'on') %}
--shape-animation: spin 1s linear infinite;
{% endif %}
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Glassmorphism Effect:
card_mod:
style: |
ha-card {
background: rgba(255, 255, 255, 0.05) !important;
backdrop-filter: blur(10px);
-webkit-backdrop-filter: blur(10px);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 16px;
}
Conditional Styling with Jinja2:
card_mod:
style: |
ha-card {
{% if is_state('binary_sensor.motion', 'on') %}
border: 2px solid #ff9800;
animation: pulse 1s ease-in-out infinite;
{% else %}
border: 1px solid rgba(255,255,255,0.1);
{% endif %}
}
@keyframes pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.7; }
}
Card-Mod in Themes (Apply Globally):
# themes/my-theme.yaml
my-theme:
card-mod-theme: my-theme
# Style all cards
card-mod-card-yaml: |
.: |
ha-card {
border-radius: 12px;
box-shadow: 0 4px 20px rgba(0,0,0,0.2);
}
# Style the sidebar
card-mod-root-yaml: |
ha-sidebar {
background: #1a1a2e !important;
}
Layout-Card (Responsive Grids)
Basic Grid Layout:
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: repeat(3, 1fr)
grid-gap: 16px
cards:
- type: custom:mushroom-light-card
entity: light.living_room
- type: custom:mushroom-light-card
entity: light.kitchen
- type: custom:mushroom-light-card
entity: light.bedroom
Responsive Layout with Media Queries:
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: repeat(4, 1fr)
grid-gap: 16px
mediaquery:
# Tablet (768px - 1024px)
"(max-width: 1024px)":
grid-template-columns: repeat(3, 1fr)
# Large phone (480px - 768px)
"(max-width: 768px)":
grid-template-columns: repeat(2, 1fr)
# Small phone (< 480px)
"(max-width: 480px)":
grid-template-columns: 1fr
cards:
- type: custom:mushroom-light-card
entity: light.living_room
# ... more cards
Grid with Spanning Cards:
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: repeat(4, 1fr)
grid-template-rows: auto
grid-gap: 16px
cards:
# Weather spans 2 columns
- type: weather-forecast
entity: weather.home
view_layout:
grid-column: span 2
# Graph spans full width
- type: custom:mini-graph-card
entities:
- sensor.temperature
view_layout:
grid-column: span 4
# Regular cards
- type: custom:mushroom-light-card
entity: light.living_room
ApexCharts Card (Advanced Data Visualization)
Basic Line Chart:
type: custom:apexcharts-card
header:
show: true
title: Temperature History
show_states: true
colorize_states: true
graph_span: 24h
series:
- entity: sensor.living_room_temperature
name: Living Room
stroke_width: 2
color: '#5294E2'
- entity: sensor.outdoor_temperature
name: Outside
stroke_width: 2
color: '#ff9800'
Radial Bar (Gauge Style):
type: custom:apexcharts-card
chart_type: radialBar
header:
show: false
series:
- entity: sensor.cpu_usage
name: CPU
color: '#5294E2'
apex_config:
plotOptions:
radialBar:
hollow:
size: '60%'
dataLabels:
name:
show: true
fontSize: '14px'
value:
show: true
fontSize: '24px'
Energy Usage (Area Chart with Gradient):
type: custom:apexcharts-card
header:
show: true
title: Energy Today
show_states: true
graph_span: 24h
span:
start: day
series:
- entity: sensor.energy_usage
type: area
stroke_width: 2
color: '#4CAF50'
opacity: 0.3
curve: smooth
statistics:
type: state
period: hour
apex_config:
chart:
height: 200
fill:
type: gradient
gradient:
shadeIntensity: 0.8
opacityFrom: 0.7
opacityTo: 0.2
Multi-Series with Comparison:
type: custom:apexcharts-card
header:
show: true
title: Temperature Comparison
graph_span: 7d
span:
start: week
series:
- entity: sensor.indoor_temp
name: Indoor
group_by:
func: avg
duration: 1d
- entity: sensor.outdoor_temp
name: Outdoor
group_by:
func: avg
duration: 1d
apex_config:
chart:
type: bar
plotOptions:
bar:
horizontal: false
columnWidth: '60%'
Mini-Graph-Card (Simple Clean Graphs)
Basic Graph:
type: custom:mini-graph-card
entities:
- sensor.living_room_temperature
name: Living Room Temp
hours_to_show: 24
points_per_hour: 2
line_width: 2
Multiple Entities with Styling:
type: custom:mini-graph-card
entities:
- entity: sensor.living_room_temp
name: Living Room
color: '#5294E2'
- entity: sensor.bedroom_temp
name: Bedroom
color: '#ff9800'
show_state: true
state_adaptive_color: true
name: Home Temperatures
hours_to_show: 24
line_width: 2
font_size: 75
show:
labels: true
labels_secondary: true
extrema: true
average: true
fill: fade
Bar Graph for Daily Stats:
type: custom:mini-graph-card
entities:
- entity: sensor.daily_energy
name: Energy
hours_to_show: 168
aggregate_func: max
group_by: date
show:
graph: bar
state: true
name: true
Floor Plan Dashboard (Picture Elements)
Basic Floor Plan Setup:
type: picture-elements
image: /local/floorplan/home.png
elements:
# Light icons
- type: state-icon
entity: light.living_room
tap_action:
action: toggle
style:
top: 45%
left: 30%
"--paper-item-icon-active-color": gold
# Temperature label
- type: state-label
entity: sensor.living_room_temp
style:
top: 50%
left: 30%
color: white
font-size: 12px
# Room navigation
- type: icon
icon: mdi:sofa
tap_action:
action: navigate
navigation_path: '#living-room'
style:
top: 55%
left: 30%
color: var(--primary-color)
Interactive Floor Plan with Overlays:
type: picture-elements
image: /local/floorplan/home_dark.png
elements:
# Light overlay (shows when on)
- type: image
entity: light.living_room
tap_action:
action: toggle
state_image:
"on": /local/floorplan/overlays/living_room_light.png
"off": /local/floorplan/transparent.png
style:
top: 50%
left: 50%
width: 100%
opacity: 0.6
# Motion indicator
- type: conditional
conditions:
- entity: binary_sensor.living_room_motion
state: "on"
elements:
- type: icon
icon: mdi:motion-sensor
style:
top: 40%
left: 35%
color: '#ff9800'
"--mdc-icon-size": 24px
3D Floor Plan Tips:
- Create in SweetHome3D (free software)
- Export as PNG with transparent background
- Create "light on" overlays for each room
- Use conditional elements for dynamic states
- Layer images for lighting effects
Animations & Effects
Spinning Icon (Fan, Loading):
card_mod:
style:
mushroom-shape-icon$: |
.shape {
--shape-animation: spin 1s linear infinite;
}
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
Pulsing Glow (Alerts):
card_mod:
style: |
ha-card {
animation: glow 2s ease-in-out infinite;
}
@keyframes glow {
0%, 100% { box-shadow: 0 0 5px rgba(255,152,0,0.5); }
50% { box-shadow: 0 0 20px rgba(255,152,0,0.8); }
}
Color Transition:
card_mod:
style: |
ha-card {
transition: background-color 0.3s ease, transform 0.2s ease;
}
ha-card:hover {
transform: translateY(-2px);
background-color: rgba(255,255,255,0.1);
}
Breathing Effect:
card_mod:
style: |
ha-card {
animation: breathe 3s ease-in-out infinite;
}
@keyframes breathe {
0%, 100% { opacity: 1; }
50% { opacity: 0.85; }
}
Gradient Animation:
card_mod:
style: |
ha-card {
background: linear-gradient(270deg, #667eea, #764ba2, #f093fb);
background-size: 600% 600%;
animation: gradientShift 10s ease infinite;
}
@keyframes gradientShift {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
Complete Dashboard Examples
Mobile-First Home View:
title: Home
views:
- title: Home
path: home
type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 1fr
grid-gap: 8px
mediaquery:
"(min-width: 768px)":
grid-template-columns: repeat(2, 1fr)
"(min-width: 1200px)":
grid-template-columns: repeat(3, 1fr)
cards:
# Status chips at top
- type: custom:mushroom-chips-card
chips:
- type: weather
entity: weather.home
- type: entity
entity: person.john
- type: entity
entity: alarm_control_panel.home
view_layout:
grid-column: 1 / -1
# Room cards
- type: custom:mushroom-template-card
entity: light.living_room
primary: Living Room
secondary: "{{ states('sensor.living_room_temp') }}°F"
icon: mdi:sofa
tap_action:
action: navigate
navigation_path: '#living-room'
# More room cards...
Tablet Wall Dashboard:
title: Wall Panel
views:
- title: Main
path: main
panel: true
cards:
- type: custom:layout-card
layout_type: grid
layout:
grid-template-columns: 2fr 1fr
grid-template-rows: auto 1fr
grid-gap: 16px
height: 100vh
cards:
# Left side - Floor plan
- type: picture-elements
image: /local/floorplan.png
view_layout:
grid-row: span 2
elements:
# ... floor plan elements
# Right side - Quick controls
- type: vertical-stack
cards:
- type: custom:mushroom-chips-card
# ... chips
- type: horizontal-stack
cards:
# ... room buttons
# Right side bottom - Weather & info
- type: vertical-stack
cards:
- type: weather-forecast
entity: weather.home
- type: custom:mini-graph-card
entities:
- sensor.outdoor_temp
Dashboard YAML Mode Setup
Enable YAML Mode:
# configuration.yaml
lovelace:
mode: yaml
resources:
- url: /hacsfiles/button-card/button-card.js
type: module
- url: /hacsfiles/lovelace-card-mod/card-mod.js
type: module
- url: /hacsfiles/lovelace-mushroom/mushroom.js
type: module
- url: /hacsfiles/bubble-card/bubble-card.js
type: module
- url: /hacsfiles/lovelace-layout-card/layout-card.js
type: module
- url: /hacsfiles/apexcharts-card/apexcharts-card.js
type: module
- url: /hacsfiles/mini-graph-card/mini-graph-card-bundle.js
type: module
Dashboard Structure:
# ui-lovelace.yaml
title: My Home
button_card_templates: !include button_card_templates.yaml
views:
- !include views/home.yaml
- !include views/lights.yaml
- !include views/climate.yaml
- !include views/security.yaml
Dashboard Troubleshooting
| Issue | Solution |
|---|---|
| Custom card not showing | Clear browser cache, check Resources in dashboard settings |
| card-mod styles not working | Ensure card-mod is loaded as module, check Shadow DOM paths |
| Pop-up not opening | Verify hash matches exactly (case-sensitive) |
| Layout not responsive | Check mediaquery syntax, use browser dev tools to test |
| Animations choppy | Reduce animation complexity, check device performance |
| Theme not applying | Restart HA after theme changes, check YAML syntax |
| Icons not spinning | Use correct Shadow DOM selector for card type |
| Graphs not loading | Check entity exists, verify time range has data |
Backup & Restore
Backup Locations
# HA OS - Full backup
/backup/
# Manual backup targets
/config/ # All configuration
/config/.storage/ # Entity/device registries
/config/custom_components/ # Custom integrations
Backup Commands
Cross-Platform (Python - Recommended for Windows):
# Use the Python backup script (works on Windows, macOS, Linux)
python scripts/backup.py /path/to/config
# Specify custom backup destination
python scripts/backup.py /path/to/config /path/to/backups
# Windows example
python scripts/backup.py C:\Users\user\homeassistant C:\backups
# The script automatically:
# - Excludes logs, databases, cache files
# - Creates timestamped .tar.gz archives
# - Cleans up old backups (keeps last 10)
# - Shows compression statistics
macOS/Linux (Bash):
# Create config backup
tar -czvf ha-config-backup-$(date +%Y%m%d).tar.gz \
--exclude='home-assistant.log*' \
--exclude='.storage' \
--exclude='tts' \
/config/
# Backup with storage (includes registries)
tar -czvf ha-full-backup-$(date +%Y%m%d).tar.gz /config/
# Or use the bash script (macOS/Linux only)
bash scripts/backup.sh /path/to/config
Restore Process
- Stop Home Assistant
- Backup current config (safety)
- Extract backup to /config/
- Restart Home Assistant
- Verify all integrations load
Installation-Specific Notes
Home Assistant OS (Recommended)
- Full Supervisor support
- Add-ons available
- Config at
/config/or/homeassistant/ - SSH via Terminal add-on or SSH add-on
Home Assistant Container (Docker)
- No Supervisor/Add-ons
- Config mounted from host
- Example:
-v /path/to/config:/config
Home Assistant Core (Python venv)
- Manual Python installation
- No Supervisor/Add-ons
- Config typically at
~/.homeassistant/
When to Use This Skill
Configuration & Administration:
- "Check my Home Assistant configuration"
- "Find YAML errors in my config"
- "What's the state of my living room lights?"
- "Turn on the bedroom fan"
- "Create an automation for motion lights"
- "Debug why my automation isn't working"
- "Review my HA security settings"
- "Help me set up a template sensor"
- "What's in my Home Assistant logs?"
- "Backup my Home Assistant configuration"
- "List all my entities"
- "Call a service via the API"
Dashboard Design & Customization:
- "Create a beautiful dashboard for my tablet"
- "Help me design a mobile-friendly dashboard"
- "Set up Mushroom cards for my lights"
- "Create pop-ups using Bubble Card"
- "Make my cards have animations"
- "Set up a dark theme with Noctis"
- "Create a floor plan dashboard"
- "Add graphs for my temperature sensors"
- "Make my dashboard responsive for all devices"
- "Style my cards with custom CSS"
- "Create reusable button-card templates"
- "Set up ApexCharts for energy monitoring"
- "Add spinning icons when my fan is on"
- "Create a glassmorphism effect on my cards"
When NOT to Use This Skill
- ESPHome device configuration (use ESPHome skill)
- Zigbee/Z-Wave device pairing (use HA UI)
- Network/router configuration (use network tools)
- Creating 3D floor plan images (use SweetHome3D or similar)
Additional Resources
- REST API Reference - Complete API endpoint documentation
- Helper Scripts - Cross-platform Python scripts:
ha_client.py- REST API client libraryconfig_audit.py- Configuration security auditorbackup.py- Cross-platform backup (Windows/macOS/Linux)backup.sh- Bash backup script (macOS/Linux only)ssh_helper.py- Cross-platform SSH with password auth
- Official HA Docs
- HA Developer Docs
- HA Community