Home Assistant is een krachtig platform voor domotica, maar de standaard workflow heeft zo zijn beperkingen. Aanpassingen maak je vaak via de UI, configuratie staat verspreid over YAML-bestanden en .storage/ JSON files, en versiebeheer is lastig. Wat als je Home Assistant kon configureren met dezelfde principes als Infrastructure-as-Code tools zoals Terraform?
In deze post laat ik zien hoe ik een volledig git-gedreven workflow heb opgezet voor Home Assistant, compleet met validatie, deployment scripts en AI-assistentie. Alle configuratie - van automaties tot dashboards - zit in version control en wordt gedeployed met één commando.
Het Probleem met Standaard Home Assistant
Home Assistant slaat configuratie op in twee verschillende systemen:
- YAML bestanden -
automations.yaml,scripts.yaml,scenes.yaml - JSON storage -
.storage/directory met entity registry, dashboards, helpers
Het probleem? De UI schrijft naar beide locaties, en de .storage/ files zijn officieel “niet bedoeld voor handmatige bewerking”. Dit maakt version control een uitdaging.
De Oplossing: Git-Driven Configuration
Mijn aanpak: behandel alles als code, inclusief de .storage/ files. De workflow:
# Voordat je begint: sync wijzigingen van de Raspberry Pi
./deploy.sh sync
# Maak aanpassingen lokaal (via editor of AI)
# ...
# Valideer en deploy
./deploy.sh simple # Voor normale wijzigingen
./deploy.sh full # Voor wijzigingen die restart vereisen
De Deploy Script Architectuur
Het hart van de setup is deploy.sh, een unified deployment script dat de sync tussen laptop en Raspberry Pi beheert:
#!/bin/bash
# Configuratie
RPI_HOST="root@homeassistant.t.lan"
RPI_CONFIG_DIR="/config"
# Deploy modes
case "$MODE" in
sync)
# Pull changes from RPi to local
git_sync_rpi # Commit + push on RPi
git_pull_local # Pull to laptop
;;
simple)
# Deploy with hot reload
git_sync_rpi
git_pull_local
validate_config # Validation layer
git_push_local
git_pull_rpi
ha_reload_all # Hot reload via Home Assistant API
;;
full)
# Deploy with restart
ha_core_stop # Stop Home Assistant
git_sync_rpi
git_pull_local
validate_config
git_push_local
git_pull_rpi
ha_core_start # Start Home Assistant
;;
esac
Belangrijke functies:
git_sync_rpi()- Commit en push onopgeslagen wijzigingen op de RPivalidate_config()- Pre-deployment validatie (zie hieronder)ha_reload_all()- Hot reload viahass-cli
De Validatie Laag
Een van de krachtigste features is de pre-deployment validatie. Voordat configuratie naar de RPi wordt gepusht, worden deze checks uitgevoerd:
validate_config() {
# 1. Script references - voorkomen broken automations
check_script_references
# 2. Helper references - valideren input_boolean/datetime/number
check_helper_references
# 3. Dashboard entities (warnings only)
check_dashboard_entities
# 4. Ungrouped items (warnings only)
check_ungrouped_items
}
Voorbeeld output:
❌ ERROR: Scripts called but not defined:
script.old_script_name
→ automations.yaml:123: action: script.old_script_name
Continue deployment anyway? (y/N)
Dit voorkomt dat broken configuratie naar productie gaat!
Script Reference Validatie
Automatisch detecteren van missende scripts:
check_script_references() {
# Extract script calls: entity_id: script.xxx
grep -ohE 'entity_id:\s*script\.[a-z0-9_]+' \
automations.yaml scripts.yaml \
| cut -d. -f2 > called_scripts.txt
# Extract defined scripts
grep -oE '^[a-z0-9_]+:' scripts.yaml \
| sed 's/:$//' > defined_scripts.txt
# Find missing scripts
comm -23 called_scripts.txt defined_scripts.txt
}
Helper Reference Validatie
Helpers (input_boolean, input_datetime, etc.) worden opgeslagen in .storage/ files, maar de entity registry kan afwijken door hernoemen via UI:
check_helper_references() {
# Find referenced helpers in YAML
grep -ohE 'entity_id:\s*input_(boolean|datetime|number)\.[a-z0-9_]+' \
automations.yaml scripts.yaml > referenced_helpers.txt
# Extract from entity registry
grep -oE '"entity_id":"input_(boolean|datetime|number)\.[^"]+' \
.storage/core.entity_registry \
| cut -d'"' -f4 > registry_helpers.txt
# Find missing
comm -23 referenced_helpers.txt registry_helpers.txt
}
Waarom dit belangrijk is: Als je een helper hernoemt via de UI, wordt het ID in .storage/input_boolean niet aangepast, maar de mapping in core.entity_registry wel. Deze validatie voorkomt broken references.
File Structure: Infrastructure-as-Code
De repository structure volgt Infrastructure-as-Code principes:
homeassistant-config/
├── configuration.yaml # Main config met includes
├── automations.yaml # Automation rules
├── scripts.yaml # Reusable scripts
├── scenes.yaml # Lighting scenes
├── .storage/ # Tracked in git!
│ ├── core.entity_registry # Entity mappings
│ ├── core.area_registry # Room definitions
│ ├── input_boolean # Helper definitions
│ ├── input_datetime
│ ├── lovelace # Main dashboard
│ └── lovelace.dashboard_* # Custom dashboards
├── deploy.sh # Deployment orchestration
├── .validate_ungrouped.py # Validation helper
└── AGENTS.md # AI agent instructions
Belangrijk: De .storage/ directory is wel in git, in tegenstelling tot standaard aanbevelingen. Dit maakt volledige version control mogelijk.
Configuration-as-Code: YAML Patterns
Automation Structure
Automaties volgen een consistent patroon met categorisatie:
- id: '1738183906765' # Timestamp-based ID
alias: 'Wekker: papa gordijnen' # Category: Description
description: ''
triggers:
- at: input_datetime.gordijnen_open_time
trigger: time
conditions:
- condition: state
entity_id: input_boolean.wekker_actief
state: 'on'
actions:
- parallel:
- action: cover.set_cover_position
data:
position: 90
target:
floor_id: zolder
continue_on_error: true # Error handling!
- action: media_player.unjoin
target:
entity_id: media_player.sonos_slaapkamer
continue_on_error: true
mode: single
Best practices:
continue_on_error: truevoor non-critical actionsparallel:voor onafhankelijke acties- Dynamische tijden via
input_datetimehelpers - Categorieën in alias:
Wekker:,Gordijn:,Alert:, etc.
Scripts: Reusable Building Blocks
Scripts fungeren als herbruikbare functies:
sonos_group_all:
sequence:
- data:
group_members:
- media_player.sonos_emma
- media_player.sonos_keuken
- media_player.sonos_melle
action: media_player.join
target:
device_id: 81d62bd216086081f4dc50b2e19ec67e
- action: media_player.volume_set
data:
volume_level: 0.4
target:
device_id:
- d6d9cbd37ee21e9b8fe20c4e48943481
- 81d62bd216086081f4dc50b2e19ec67e
alias: 'Sonos: groepeer alles'
description: ''
Scripts worden aangeroepen vanuit automaties:
actions:
- action: script.sonos_group_all
continue_on_error: true
Helpers: Dynamic Configuration
Helpers worden gedefinieerd in .storage/input_boolean (JSON):
{
"version": 1,
"key": "input_boolean",
"data": {
"items": [
{
"id": "wekker_aan",
"name": "Wekker: actief",
"category": "01K9FCWM0025121PPKZS38XY90"
},
{
"id": "gordijnen_open",
"name": "Gordijn: ochtend",
"category": "01K9FAV5XQW298AAT2B2ACBJQD"
}
]
}
}
Categorieën zorgen voor groepering in de UI. De validation script controleert op uncategorized items:
def find_ungrouped_helpers(file_path, helper_type):
with open(file_path, 'r') as f:
data = json.load(f)
ungrouped = []
for item in data.get('data', {}).get('items', []):
if item.get('category') is None:
ungrouped.append({
'id': item.get('id'),
'name': item.get('name')
})
if ungrouped:
print(f"⚠️ {helper_type} without category:")
for item in ungrouped:
print(f" ⚠️ {helper_type}.{item['id']}")
AI-Assisted Configuration met AGENTS.md
Een cruciale component is AGENTS.md: een gedetailleerde instructieset voor AI coding agents. Dit maakt AI-assisted configuratie mogelijk:
# AGENTS.md - Home Assistant Configuration Repository
## Automation Structure
- id: timestamp-based unique ID
- alias: 'Category: Description' (colon-prefixed)
- Use 'trigger' key, not 'platform'
- Use 'action' key, not 'service'
- Always add continue_on_error: true for non-critical actions
## Entity ID Naming
# Pattern: domain.location_device_descriptor
light.keuken_tafel_links
media_player.sonos_slaapkamer
cover.gordijnen_group_alles
Met deze instructies kan een AI agent:
- Nieuwe automaties toevoegen met correct formaat
- Scripts creëren volgens naming conventions
- Validatie draaien voor deployment
- Git workflow uitvoeren
Voorbeeld interactie:
User: "Voeg een automatisering toe die 's avonds om 22:00
alle lampen uitdoet als ik thuis ben"
AI Agent:
1. Leest AGENTS.md voor conventions
2. Genereert automation met juiste structure
3. Voegt toe aan automations.yaml
4. Draait ./deploy.sh validate
5. Bij succes: ./deploy.sh simple
Dashboard Configuration-as-Code
Dashboards worden opgeslagen in .storage/lovelace* files. Convention: gebruik auto-entities voor dynamische lijsten:
{
"type": "custom:auto-entities",
"card": {
"type": "entities",
"title": "Batterijen Laag",
"state_color": true
},
"filter": {
"include": [
{
"integration": "hue",
"attributes": {
"device_class": "battery"
}
}
]
},
"sort": {
"method": "state",
"numeric": true
},
"show_empty": false
}
Voordelen:
- Nieuwe devices worden automatisch toegevoegd
- Geen handmatige maintenance bij toevoegen/verwijderen
- Consistente filtering en sorting
Entity Registry: De Waarheid
Een veelvoorkomend probleem: je hernoemt een entity via de UI, maar in de YAML staat nog de oude naam. Hoe kan dit?
De core.entity_registry bevat de mapping:
{
"entity_id": "input_boolean.wekker_actief",
"unique_id": "wekker_aan",
"original_name": "Wekker: actief",
"name": null
}
unique_id- De ID in.storage/input_booleanentity_id- De daadwerkelijke entity ID in Home Assistantname- UI override (null = gebruik original_name)
Debugging tip: Als een entity “niet bestaat”, check eerst de entity registry:
grep -A10 '"unique_id":"wekker_aan"' .storage/core.entity_registry
Pre-Commit Hooks: Validation op Steroïden
Voor extra veiligheid kun je git pre-commit hooks toevoegen:
#!/bin/bash
# .git/hooks/pre-commit
# Validate YAML syntax
yamllint automations.yaml scripts.yaml scenes.yaml || exit 1
# Run configuration validation
./deploy.sh validate || exit 1
# Check for secrets
if git diff --cached | grep -i "password\|token\|api_key"; then
echo "ERROR: Possible secret detected!"
exit 1
fi
Terraform-Style Apply Workflow
De volledige workflow lijkt op Terraform:
# 1. Plan: Sync remote state
./deploy.sh sync
# 2. Edit: Make changes locally
vim automations.yaml
# 3. Validate: Check configuration
./deploy.sh validate
# 4. Apply: Deploy to production
./deploy.sh simple
Output tijdens deployment:
==> Syncing changes from RPi...
==> RPi has no uncommitted changes
==> Pulling changes to local...
==> Local pull complete
==> Validating configuration...
==> Configuration validation passed
==> Pushing local changes...
==> Committing local changes...
==> Local changes pushed
==> Pulling changes on RPi...
==> RPi pull complete
==> Reloading Home Assistant configuration...
==> Home Assistant configuration reloaded
==> SIMPLE deploy complete!
Conflict Handling: Merge Strategies
Omdat zowel lokaal als op de RPi wijzigingen gemaakt kunnen worden, zijn merge conflicts mogelijk:
check_conflicts() {
# Check for unmerged paths
if git ls-files -u | grep -q .; then
return 1
fi
# Check for conflict markers
if git diff --name-only | xargs grep -l "<<<<<<\|>>>>>>"; then
return 1
fi
return 0
}
Bij conflicts:
ERROR: Merge conflicts detected!
Conflicting files:
.storage/core.entity_registry
To resolve:
1. Edit the conflicting files
2. Run: git add <resolved-files>
3. Re-run: ./deploy.sh simple
Testing: Template Editor & Developer Tools
Home Assistant heeft ingebouwde test tools:
Template Editor (Developer Tools > Template):
# Test Jinja2 templates
Service Calls (Developer Tools > Actions):
action: script.sonos_group_all
data: {}
Deze kunnen geïntegreerd worden in test scripts:
# Test automation triggers
hass-cli event fire test_event --data '{"entity_id": "light.test"}'
# Check if entity exists
hass-cli state get input_boolean.wekker_actief
Remote Sync: Cron op de RPi
Op de Raspberry Pi draait een cron job die automatisch sync:
# /config/scripts/git_sync.sh (op RPi)
#!/bin/bash
cd /config
git add .
git commit -m "Auto-sync from RPi: $(date)"
git push origin main
Cron entry:
# Sync every hour
0 * * * * /config/scripts/git_sync.sh
Dit zorgt ervoor dat UI wijzigingen op de RPi automatisch naar git gaan.
Security: Secrets Management
Gevoelige data gaat nooit in git:
# .gitignore
secrets.yaml
.env.secrets
*.key
*.pem
Secrets bestand (.env.secrets):
export HASS_SERVER=http://homeassistant.t.lan:8123
export HASS_TOKEN=eyJ0eXAiOiJKV1QiLCJ...
Usage in deploy script:
load_secrets() {
if [[ -f "$SECRETS_FILE" ]]; then
source "$SECRETS_FILE"
return 0
fi
}
ha_reload_all() {
check_secrets
hass-cli service call homeassistant.reload_all
}
Voordelen van deze Aanpak
Version Control:
- Volledige history van alle wijzigingen
- Rollback naar eerdere versies
- Branching voor experimenten
Validatie:
- Pre-deployment checks voorkomen errors
- Broken references worden gevangen
- Consistent formatting
Reproduceerbaar:
- Configuratie is portable
- Disaster recovery: clone repo en deploy
- Documentatie in code zelf
AI-Assisted:
- Agents kunnen veilig wijzigingen maken
- Validation voorkomt fouten
- Snellere iteratie
Testing:
- Test lokaal voor deployment
- Feature branches voor grote wijzigingen
- Automated validation
Nadelen en Trade-offs
Complexity:
- Meer setup dan standaard workflow
- Git kennis vereist
- Deploy script onderhoud
Storage Files:
- Officieel “not for editing”
- Mogelijk breaking changes in HA updates
- Merge conflicts kunnen complex zijn
Sync Delays:
- UI wijzigingen niet instant in git
- Cron interval (1 uur in mijn setup)
- Conflicts bij gelijktijdige edits
Conclusie
Door Home Assistant te behandelen als Infrastructure-as-Code krijg je:
- Version control voor alle configuratie
- Pre-deployment validatie die errors voorkomt
- Git-driven workflow met sync tussen devices
- AI-assistentie via gestructureerde instructies
- Reproduceerbare configuratie
De setup vereist initiële investering, maar betaalt zich terug in:
- Minder debugging tijd
- Snellere iteratie met AI
- Veiligere deployments
- Betere documentatie
Voor teams of mensen die Infrastructure-as-Code gewend zijn, voelt deze workflow natuurlijk. En met AI agents die de AGENTS.md kunnen lezen, wordt configuratie wijzigen zo simpel als: “voeg een automatisering toe voor X” - de agent regelt formatting, validatie en deployment.
Volgende stappen:
- CI/CD pipeline met GitHub Actions
- Automated testing met HA test fixtures
- Multi-environment setup (test/prod)
- Metrics en monitoring van deployments
De volledige code is beschikbaar als referentie voor wie een vergelijkbare setup wil bouwen. Happy automating!