
Ha Addon
- 72 installs
- 14 repo stars
- Updated April 20, 2026
- nodnarbnitram/claude-code-extensions
Helps with ai & agent building tasks during AI-assisted development.
About
ha-addon is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- ha-addon
- AI & Agent Building
- AI-coding skill
Ha Addon by the numbers
- 72 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #5,635 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 4, 2026 (Skillselion catalog sync)
npx skills add https://github.com/nodnarbnitram/claude-code-extensions --skill ha-addonAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 72 |
|---|---|
| repo stars | ★ 14 |
| Last updated | April 20, 2026 |
| Repository | nodnarbnitram/claude-code-extensions ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Home Assistant Add-On Development
Expert guidance for building, configuring, and publishing Home Assistant add-ons with Docker, Supervisor integration, and multi-architecture support.
Before You Start
This skill prevents common Home Assistant add-on development errors:
| Issue | Symptom | Solution |
|---|---|---|
| Permission errors | Permission denied on supervisor API calls | Use correct SUPERVISOR_TOKEN and API endpoints |
| Configuration validation | Add-on won't load | Validate config.yaml schema before publishing |
| Docker base image errors | Missing dependencies in runtime | Use official Home Assistant base images (ghcr.io/home-assistant) |
| Ingress misconfiguration | Web UI not accessible through HA | Configure nginx reverse proxy correctly |
| Multi-arch build failures | Add-on only works on one architecture | Set up build.yaml with architecture matrix |
Quick Start: Create an Add-On from Scratch
Step 1: Create the Add-On Directory Structure
mkdir -p my-addon/{rootfs,rootfs/etc/s6-overlay/s6-rc.d/service-name}
cd my-addonWhy this matters: Home Assistant expects specific directory layouts. The rootfs/ contains your actual application files that get packaged into the Docker image.
Step 2: Create config.yaml
---
name: My Custom Add-On
description: My awesome Home Assistant add-on
version: 1.0.0
slug: my-addon
image: ghcr.io/home-assistant/{arch}-addon-my-addon
arch:
- amd64
- armv7
- aarch64
ports:
8080/tcp: null
options:
debug: false
schema:
debug: bool
permissions:
- homeassistant # Read/write Home Assistant core dataWhy this matters: This is your add-on's manifest. The slug becomes the internal identifier and determines where configuration is stored.
Step 3: Create the Dockerfile
FROM ghcr.io/home-assistant/amd64-base:latest
# Install dependencies
RUN apk add --no-cache python3 py3-pip
# Copy application
COPY rootfs /
# Set working directory
WORKDIR /app
# Install Python packages if needed
RUN if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
# Run using S6 overlay
CMD ["/init"]Why this matters: Using Home Assistant base images includes critical runtime components (S6 overlay, bashio helpers, supervisor integration).
Step 4: Create S6 Service Script
Create rootfs/etc/s6-overlay/s6-rc.d/service-name/run:
#!/command/execlineb -P
foreground { echo "Starting my add-on..." }
/app/my-serviceMake it executable:
chmod +x rootfs/etc/s6-overlay/s6-rc.d/service-name/runWhy this matters: S6 overlay is Home Assistant's init system. It manages service startup, logging, and graceful shutdown.
Critical Rules
✅ Always Do
- ✅ Use official Home Assistant base images (ghcr.io/home-assistant/{arch}-base)
- ✅ Include all supported architectures in config.yaml (amd64, armv7, aarch64)
- ✅ Use bashio helper functions for common operations (bashio::log::info, bashio::addon::option)
- ✅ Validate config.yaml schema before releasing
- ✅ Document configuration options in the schema section
- ✅ Include addon_uuid in logs for debugging
❌ Never Do
- ❌ Don't hardcode paths - use bashio to get configuration directory (/data/)
- ❌ Don't run services as root unless absolutely necessary (set USER in Dockerfile)
- ❌ Don't call supervisor API without SUPERVISOR_TOKEN
- ❌ Don't ignore SIGTERM signals - implement graceful shutdown
- ❌ Don't assume one architecture - use {arch} placeholder in image names
- ❌ Don't store data outside /data/ - Home Assistant won't persist it
Common Mistakes
❌ Wrong: Hardcoded paths
#!/bin/bash
CONFIG_PATH="/config/my-addon"✅ Correct: Using bashio for configuration
#!/command/execlineb -P
CONFIG_PATH=${"$(bashio::addon::config_path)"}Why: bashio handles path resolution and ensures your add-on works in any Home Assistant installation.
Configuration Reference
config.yaml Structure
---
name: String # Display name
description: String # Short description
version: String # Semantic version (1.0.0)
slug: String # URL-safe identifier
image: String # Docker image URL with {arch} placeholder
arch:
- amd64|armv7|aarch64|armhf|i386 # Supported architectures
ports:
8080/tcp: null # TCP port (null=internal only, number=external)
53/udp: 53 # UDP with external port mapping
devices:
- /dev/ttyACM0 # Device access
services:
- mysql # Depends on other service
options:
debug: false # User configuration options
log_level: info
schema:
debug: bool # Configuration validation schema
log_level:
- debug
- info
- warning
- error
permissions:
- homeassistant # Read/write HA config
- hassio # Full supervisor API access
- admin # Broad system access
- backup # Backup/restore operations
environment:
NODE_ENV: production
webui: http://[HOST]:[PORT:8080] # Web UI URL pattern
ingress: true # Enable ingress proxy
ingress_port: 8080 # Internal port for ingress
ingress_entry: / # URL path for ingress entryKey settings:
slug: Used internally and in supervisor API callsarch: List all supported architectures or builds failimage: Must use {arch} placeholder for dynamic buildsoptions: User-configurable settingspermissions: Controls supervisor API access levelingress: Enables reverse proxy for web UIs
Common Patterns
Using bashio for Logging
#!/command/execlineb -P
foreground { bashio::log::info "Add-on started" }
foreground { bashio::log::warning "Low disk space" }
foreground { bashio::log::error "Failed to connect" }Accessing Configuration Options
#!/command/execlineb -P
define DEBUG "$(bashio::addon::option 'debug')"
define LOG_LEVEL "$(bashio::addon::option 'log_level')"
if { test "${DEBUG}" = "true" }
bashio::log::debug "Debug mode enabled"Supervisor API Communication
#!/bin/bash
# Get addon info
curl -X GET \
-H "Authorization: Bearer $SUPERVISOR_TOKEN" \
http://supervisor/addons/self/info | jq .
# Send notification
curl -X POST \
-H "Authorization: Bearer $SUPERVISOR_TOKEN" \
-H "Content-Type: application/json" \
-d '{"message":"Warning message"}' \
http://supervisor/notifications/createMulti-Arch Docker Build
Create build.yaml:
build_from:
amd64: ghcr.io/home-assistant/amd64-base:latest
armv7: ghcr.io/home-assistant/armv7-base:latest
aarch64: ghcr.io/home-assistant/aarch64-base:latest
armhf: ghcr.io/home-assistant/armhf-base:latest
codenotary: your-notary-id # Optional code signingIngress Configuration for Web UIs
ingress: true
ingress_port: 8080
ingress_entry: /
# Optional ingress_stream for streaming endpointsInside your app, use correct reverse proxy headers:
# nginx configuration in your app
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Host $host;
proxy_pass http://localhost:8080;
}Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
| Add-on fails to start | Missing S6 service files | Create /etc/s6-overlay/s6-rc.d/service-name/ with run executable |
| Supervisor API returns 401 | Invalid SUPERVISOR_TOKEN | Verify token is set by Home Assistant (check logs with addon_uuid) |
| Configuration not persisting | Saving outside /data/ | Always use bashio::addon::config_path or /data/ for persistence |
| Port already in use | Multiple services on same port | Check configuration - each service needs unique port |
| Architecture mismatch | {arch} placeholder not used | Use exact placeholder in image field: ghcr.io/home-assistant/{arch}-base |
| Build fails with "Unknown architecture" | config.yaml lists unsupported arch | Use only: amd64, armv7, aarch64, armhf, i386 |
Supervisor API Endpoints
# Authentication: Pass SUPERVISOR_TOKEN header
# Base URL: http://supervisor
GET /addons/self/info # Get current add-on details
POST /addons/self/restart # Restart this add-on
GET /addons/installed # List installed add-ons
GET /info # System information
POST /notifications/create # Send notification to user
GET /config/homeassistant # Read Home Assistant config
# Example with bashio
bashio::addon::self_info # Helper function for self infoDependencies
Required
| Package | Version | Purpose |
|---|---|---|
| Home Assistant | 2024.1+ | Add-on platform and supervisor |
| Docker | Latest | Container runtime |
| S6 Overlay | 3.x | Init system (included in base images) |
Optional
| Package | Version | Purpose |
|---|---|---|
| bashio | Latest | Helper functions (included in base images) |
| python3 | 3.9+ | Python-based add-ons |
| nodejs | 18+ | Node.js-based add-ons |
Official Documentation
- Home Assistant Add-On Development
- Add-On Configuration Reference
- Supervisor Development
- bashio Helper Functions
- S6 Overlay Documentation
Troubleshooting
Add-on Won't Start
Symptoms: Add-on shows as "Not running" or "Unknown"
Solution:
# Check logs
docker logs addon_name_latest # Or use HA UI: Settings > System > Logs
# Common causes:
# 1. Invalid config.yaml syntax
# 2. Missing S6 service files
# 3. Dockerfile can't find base image
# 4. Permission denied on rootfs filesSupervisor API Returns 401
Symptoms: API calls fail with "Unauthorized"
Solution:
# Verify SUPERVISOR_TOKEN is set
echo $SUPERVISOR_TOKEN
# Check add-on logs for token errors
# Token is automatically injected by Home Assistant
# Verify permissions in config.yaml
# If calling hassio endpoints, add: permissions: [hassio]Configuration Not Saving
Symptoms: Options are lost after restart
Solution:
# Always save to /data/ or use bashio
CONFIG_PATH="$(bashio::addon::config_path)" # Returns /data/
echo "my_value=123" > "${CONFIG_PATH}/settings.json"
# Verify /data/ exists and is writable
ls -la /data/Ingress Web UI Not Accessible
Symptoms: Ingress URL returns 502 or blank page
Solution:
# 1. Verify service is listening on correct port
netstat -tlnp | grep 8080
# 2. Check reverse proxy headers in app config
# X-Forwarded-For, X-Forwarded-Proto must be set
# 3. Verify ingress settings in config.yaml
ingress: true
ingress_port: 8080
ingress_entry: /Build Fails with Architecture Error
Symptoms: "Unknown architecture" or "Image not found"
Solution:
# Check config.yaml has valid arch values
arch:
- amd64 # x86 64-bit
- armv7 # 32-bit ARM (Pi 2/3)
- aarch64 # 64-bit ARM (Pi 4+)
- armhf # 32-bit ARM (older devices)
- i386 # 32-bit x86 (rare)
# Dockerfile must use {arch} placeholder
FROM ghcr.io/home-assistant/{arch}-base:latestSetup Checklist
Before publishing your add-on, verify:
- [ ] config.yaml has valid YAML syntax (use online YAML validator)
- [ ] All listed architectures are supported (amd64, armv7, aarch64, armhf, i386)
- [ ] Dockerfile uses official Home Assistant base image
- [ ] S6 service files exist and are executable (chmod +x)
- [ ] All configuration options are documented in schema
- [ ] No hardcoded paths (use bashio helpers)
- [ ] Permissions field lists required supervisor API access
- [ ] Tested on at least amd64 and ARM architecture
- [ ] Logs use bashio::log functions
- [ ] Graceful shutdown on SIGTERM implemented
- [ ] /data/ used for all persistent data
- [ ] Ingress working if web UI is provided
- [ ] README includes installation and usage instructions
Creating a Repository
To publish multiple add-ons:
1. Create Repository Structure
mkdir my-addon-repo
cd my-addon-repo2. Create repository.yaml
---
name: My Add-On Repository
url: https://github.com/username/my-addon-repo
maintainer: Your Name <email@example.com>3. Add Add-Ons
my-addon-repo/
├── repository.yaml
├── my-addon-1/
│ ├── config.yaml
│ ├── Dockerfile
│ └── rootfs/
└── my-addon-2/
├── config.yaml
├── Dockerfile
└── rootfs/4. Push to GitHub
Add the repository URL to Home Assistant to make add-ons discoverable.
Advanced: Publishing to GitHub Container Registry
For private repositories or multi-architecture builds:
# Build and push for all architectures
docker buildx build \
--platform linux/amd64,linux/arm/v7,linux/arm64/v8 \
-t ghcr.io/username/my-addon:1.0.0 \
--push .Related Skills
docker-configs- Docker fundamentals and best practicesesphome-config-helper- Related IoT device integration patternshome-assistant-automation- Home Assistant automation and scripting
bashio Helper Functions Reference
bashio is a set of bash helper functions included in Home Assistant base images. It simplifies common operations for add-ons, eliminating the need to hardcode paths or directly call APIs.
GitHub: https://github.com/hassio-addons/bashio Included in: All official Home Assistant base images
Quick Start
Use bashio in your S6 service scripts (rootfs/etc/s6-overlay/s6-rc.d/*/run):
#!/command/execlineb -P
foreground { bashio::log::info "Starting my service" }
/app/my-serviceOr in standard bash scripts (S6 newer versions):
#!/bin/bash
source /usr/lib/bashio.sh
bashio::log::info "Service started"Logging Functions
All logging goes to Home Assistant logs (visible in UI).
Basic Logging
bashio::log::info "Informational message"
bashio::log::notice "Important notice"
bashio::log::warning "Warning message"
bashio::log::error "Error message"
bashio::log::debug "Debug message (only if debug enabled)"
bashio::log::red "Red colored text"
bashio::log::green "Green colored text"
bashio::log::yellow "Yellow colored text"With Context
# Include add-on UUID for debugging
bashio::log::info "[$(bashio::addon::id)] Service started"
# Include configuration value
bashio::log::info "Using log_level: $(bashio::addon::option 'log_level')"Configuration Access
Read Options
# Get single option
OPTION="$(bashio::addon::option 'debug')"
# Get option with default fallback
OPTION="$(bashio::addon::option 'log_level' 'info')"
# Check if option is true
if bashio::addon::option 'debug'; then
bashio::log::debug "Debug mode enabled"
fi
# Read JSON configuration
CONFIG="$(bashio::addon::option 'server')"
HOST="$(bashio::jq "${CONFIG}" '.host')"
PORT="$(bashio::jq "${CONFIG}" '.port')"Get Paths
# Configuration data path (persisted)
CONFIG_PATH="$(bashio::addon::config_path)"
# Temporary directory
TEMP_PATH="/tmp"
# Add-on UUID (unique identifier)
ADDON_UUID="$(bashio::addon::id)"
# Home Assistant configuration directory
HA_CONFIG="/config"Supervisor API Access
Addon Information
# Get current add-on info
bashio::addon::self_info
# Get all add-ons
bashio::supervisor::addons
# Check if other add-on is installed
bashio::addon::installed "my-other-addon"
# Get specific add-on info
bashio::addon::info "slug-name"Addon Lifecycle
# Restart this add-on
bashio::addon::restart
# Stop this add-on
bashio::addon::stop
# Start another add-on
bashio::addon::start "mysql"
# Reload addon (update config)
bashio::addon::reloadSupervisor Info
# System information
bashio::supervisor::info
# Home Assistant information
bashio::homeassistant::info
# Check Supervisor version
VERSION="$(bashio::supervisor::version)"
# Get update available
bashio::supervisor::update_availableNotifications
# Send notification to user
bashio::notification::send "Warning" "Something went wrong"
# With more options
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"title": "My Add-On",
"message": "Something happened",
"notification_id": "my-addon-1",
"data": {
"custom_field": "value"
}
}' \
http://supervisor/notifications/createValidation Functions
Check Conditions
# Check if string is empty
if bashio::var::is_empty "${MY_VAR}"; then
bashio::log::error "Variable is empty"
fi
# Check if string equals another
if bashio::var::equals "${MY_VAR}" "expected_value"; then
bashio::log::info "Match!"
fi
# Check if array contains element
if bashio::var::in_array "${VALUE}" "${ARRAY[@]}"; then
bashio::log::info "Found in array"
fi
# Check if file exists
if bashio::fs::file_exists "/data/config.json"; then
bashio::log::info "Config found"
fi
# Check if directory exists
if bashio::fs::directory_exists "/data/backups"; then
bashio::log::info "Backup directory found"
fiJSON/YAML Processing
JSON Utilities
# Parse JSON field
VALUE="$(bashio::jq "${JSON_DATA}" '.field_name')"
# Parse nested JSON
HOST="$(bashio::jq "${JSON_DATA}" '.server.host')"
# Parse array
ITEM="$(bashio::jq "${JSON_DATA}" '.items[0]')"
# Parse with default
VALUE="$(bashio::jq "${JSON_DATA}" '.optional_field // "default"')"
# Pretty print JSON
bashio::jq "${JSON_DATA}"YAML Utilities
# Read from YAML configuration
bashio::yaml::read_object "/config/automations.yaml"Real-World Examples
Example 1: Read Config and Log
#!/command/execlineb -P
foreground {
bashio::log::info "Add-on starting..."
}
foreground {
define DEBUG "$(bashio::addon::option 'debug')"
bashio::log::info "Debug mode: ${DEBUG}"
}
foreground {
define CONFIG_PATH "$(bashio::addon::config_path)"
bashio::log::info "Using config path: ${CONFIG_PATH}"
}
# Run service
/app/my-serviceExample 2: API Communication
#!/bin/bash
source /usr/lib/bashio.sh
# Get add-on configuration
CONFIG_PATH="$(bashio::addon::config_path)"
# Read custom config
if [ -f "${CONFIG_PATH}/settings.json" ]; then
SETTINGS="$(cat "${CONFIG_PATH}/settings.json")"
API_KEY="$(bashio::jq "${SETTINGS}" '.api_key')"
else
bashio::log::warning "Settings not found, using defaults"
API_KEY=""
fi
# Communicate with Home Assistant
HASS_INFO="$(bashio::homeassistant::info)"
VERSION="$(bashio::jq "${HASS_INFO}" '.version')"
bashio::log::info "Home Assistant version: ${VERSION}"
# Run service
/app/my-service --api-key="${API_KEY}"Example 3: Health Check Loop
#!/bin/bash
source /usr/lib/bashio.sh
# Start service in background
/app/my-service &
SERVICE_PID=$!
bashio::log::info "Service started with PID ${SERVICE_PID}"
# Monitor service
while true; do
if ! kill -0 "${SERVICE_PID}" 2>/dev/null; then
bashio::log::error "Service died unexpectedly"
exit 1
fi
if ! curl -f http://localhost:8080/health > /dev/null 2>&1; then
bashio::log::warning "Health check failed"
fi
sleep 30
done
trap "kill ${SERVICE_PID}" SIGTERMCommon Patterns
Pattern: Configuration Validation
# Ensure required options are set
if bashio::var::is_empty "$(bashio::addon::option 'api_key')"; then
bashio::log::error "api_key is required"
exit 1
fi
bashio::log::info "Configuration valid"Pattern: Debug Mode Toggle
if bashio::addon::option 'debug'; then
# Enable debug logging
export DEBUG=1
bashio::log::debug "Debug mode enabled"
else
export DEBUG=0
fiPattern: Directory Initialization
CONFIG_PATH="$(bashio::addon::config_path)"
# Create required directories
mkdir -p "${CONFIG_PATH}/data"
mkdir -p "${CONFIG_PATH}/logs"
bashio::log::info "Initialized directories in ${CONFIG_PATH}"Important Notes
Always Available Environment Variables
Automatically set by Home Assistant:
SUPERVISOR_TOKEN- API authentication tokenSUPERVISOR_HOST- Supervisor API host (alwayssupervisor)ADDON_UUID- Unique identifier for this add-on
Using SUPERVISOR_TOKEN
For direct API calls when bashio helpers don't exist:
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/installedS6 Overlay Integration
bashio is automatically sourced in S6 scripts. For standard executables:
# Source bashio in your script
source /usr/lib/bashio.sh
# Now bashio functions are available
bashio::log::info "Ready to go"Error Handling
bashio functions use exit codes:
if bashio::addon::self_info > /dev/null 2>&1; then
bashio::log::info "Successfully retrieved info"
else
bashio::log::error "Failed to get info"
fiTroubleshooting
"bashio: command not found"
Problem: bashio not available
Solution:
# Check location
ls -la /usr/lib/bashio.sh
# Source it explicitly
source /usr/lib/bashio.sh"SUPERVISOR_TOKEN not set"
Problem: Token not available in scripts
Solution:
- Token is set by Home Assistant automatically
- Make sure you're running inside the Docker container
- Check add-on permissions in config.yaml
"Permission denied" on API calls
Problem: Insufficient permissions
Solution:
# Add required permissions to config.yaml
permissions:
- homeassistant # For HA core access
- hassio # For Supervisor API
- admin # For system accessAdditional Resources
---
# Home Assistant Add-On Configuration Template
# Reference: https://developers.home-assistant.io/docs/add-ons/configuration
name: My Custom Add-On
description: A brief description of what your add-on does
version: 1.0.0
# URL-safe identifier used by Home Assistant internally
slug: my-addon
# Docker image URL - use {arch} placeholder for multi-architecture support
# Supported architectures: amd64, armv7, aarch64, armhf, i386
image: ghcr.io/home-assistant/{arch}-addon-my-addon
# List of supported architectures
arch:
- amd64 # Intel/AMD 64-bit (Desktop, NAS, most servers)
- armv7 # ARM 32-bit (Raspberry Pi 2/3)
- aarch64 # ARM 64-bit (Raspberry Pi 4/5, newer ARMs)
# Optional:
# - armhf # ARM hard-float
# - i386 # Intel/AMD 32-bit (rarely used)
# Port mappings: internal_port/protocol: external_port (null = internal only)
ports:
8080/tcp: 8080 # Expose port 8080 externally
9090/tcp: null # Internal only
53/udp: 53 # UDP port
# Format: [HOST_IP:]HOST_PORT:CONTAINER_PORT/PROTOCOL
# null = not exposed externally (internal communication only)
# USB/serial device access (if needed)
devices: []
# Example:
# devices:
# - /dev/ttyUSB0
# - /dev/ttyACM0
# Other add-on services this add-on depends on
services: []
# Example:
# services:
# - mysql
# User-configurable options (appears in UI)
options:
debug: false
log_level: info
# Configuration option schema (validation and UI generation)
schema:
debug: bool
log_level:
type: list
items:
enum:
- debug
- info
- warning
- error
# Supervisor API permissions required by this add-on
# - homeassistant: Read/write Home Assistant core data
# - hassio: Full Supervisor API access
# - admin: Broad system access
# - backup: Backup/restore operations
permissions:
- homeassistant
# Environment variables for the add-on container
environment:
NODE_ENV: production
TZ: UTC
# Web UI configuration (if add-on has a web interface)
webui: http://[HOST]:[PORT:8080]
# Ingress configuration (reverse proxy through Home Assistant)
# Set to true to access add-on UI through Home Assistant frontend
ingress: true
ingress_port: 8080 # Internal port to proxy to
ingress_entry: / # URL path for ingress (e.g., /my-addon/)
# Backup configuration (if add-on stores important data)
homeassistant_api: true # Allow access to Home Assistant API
# Changelog URL
codenotary: null
# Advanced: Audio/Video support
audio: false
video: false
# Docker image pull policy
docker_api: true
# Health check
healthcheck: true
# Example advanced configuration (uncomment as needed):
#
# # Volume mounts
# volumes:
# config:
# description: Configuration directory
# size: 2GB
#
# # Required: Add-on won't start if services are unavailable
# services:
# - mysql
#
# # Icon (relative to add-on directory)
# icon: icon.png
#
# # Logo
# logo: logo.png
#
# # Rating (1-5)
# rating: 4
#
# # Requires Home Assistant (minimum version)
# homeassistant: 2024.1.0
#
# # Add-on author
# author: Your Name <your.email@example.com>
#
# # Add-on documentation URL
# documentation: https://github.com/your-user/your-addon/blob/main/README.md
#
# # Issue tracker
# issue_tracker: https://github.com/your-user/your-addon/issues
#
# # Repository/source code
# repository: https://github.com/your-user/your-addon
#
# # Feature flags
# full_access: false
# privileged:
# - NET_ADMIN
# - NET_RAW
# Home Assistant Add-On Dockerfile Template
# Reference: https://developers.home-assistant.io/docs/add-ons
#
# CRITICAL: Use official Home Assistant base images
# Do NOT use generic Alpine/Ubuntu images without understanding dependencies
#
# Home Assistant base images include:
# - S6 overlay (process management and init system)
# - bashio (helper functions for supervisor API)
# - Root filesystem compatibility
# Use {arch} placeholder for multi-architecture support
# Supported architectures: amd64, armv7, aarch64, armhf, i386
FROM ghcr.io/home-assistant/{arch}-base:latest
# Install system packages (Alpine Linux package manager)
RUN apk add --no-cache \
python3 \
py3-pip \
ca-certificates
# Copy entire rootfs/ directory into image
# This includes S6 service definitions, configuration, application files
COPY rootfs /
# Set working directory (optional, for your application)
WORKDIR /app
# Install Python dependencies if your add-on uses Python
# RUN if [ -f requirements.txt ]; then \
# pip install --no-cache-dir -r requirements.txt; \
# fi
# Set user (security best practice - run as non-root if possible)
# USER appuser
# S6 overlay automatically starts services defined in rootfs/etc/s6-overlay/s6-rc.d/
# No need to specify CMD - S6 overlay handles process management
#
# Home Assistant automatically:
# - Passes SUPERVISOR_TOKEN environment variable
# - Sets up logging to stdout/stderr
# - Manages container lifecycle
# - Handles graceful shutdown on SIGTERM
# Health check (optional)
# HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
# CMD curl -f http://localhost:8080/health || exit 1
# Example: Application-specific setup
#
# If your add-on is Python-based:
# RUN pip install --no-cache-dir paho-mqtt requests
#
# If your add-on is Node.js-based:
# FROM ghcr.io/home-assistant/{arch}-base:latest
# RUN apk add --no-cache nodejs npm
# COPY rootfs /
# WORKDIR /app
# RUN npm install
#
# If your add-on needs system libraries:
# RUN apk add --no-cache \
# libffi-dev \
# openssl-dev \
# gcc \
# musl-dev
Home Assistant Add-On Development
Expert guidance for building, configuring, and publishing Home Assistant add-ons with Docker, Supervisor integration, and multi-architecture support.
| Status | Production Ready |
| Version | 1.0.0 |
| Last Updated | 2025-12-31 |
| Confidence | 5/5 |
What This Skill Does
Develops Home Assistant add-ons from scratch with complete Docker container support, Home Assistant Supervisor API integration, multi-architecture builds, and repository publishing. Covers the full lifecycle: project setup, configuration, Docker builds, S6 overlay services, Supervisor API communication, ingress web UI setup, and publishing to add-on repositories.
Core Capabilities
- Create add-on project structure with proper directory layouts
- Configure config.yaml with all options, permissions, and architecture support
- Build multi-architecture Docker containers using Home Assistant base images
- Set up S6 overlay services for process management and logging
- Communicate with Home Assistant Supervisor API using bashio helpers
- Configure ingress reverse proxy for web-based add-on UIs
- Validate configuration schemas before publishing
- Package and publish to GitHub-based add-on repositories
- Debug common errors (permission, network, persistence, architecture mismatches)
Auto-Trigger Keywords
Primary Keywords
Exact terms that strongly trigger this skill:
- add-on (or addon)
- supervisor (or hassio)
- home assistant docker
- ingress proxy
- bashio helper
- s6 overlay
Secondary Keywords
Related terms that may trigger in combination:
- homeassistant container
- ha docker configuration
- home assistant service
- supervisor token
- home assistant extension
- docker for home assistant
Error-Based Keywords
Common error messages that should trigger this skill:
- "Add-on won't start"
- "Supervisor API returns 401"
- "Permission denied on supervisor"
- "Configuration not saving"
- "Unknown architecture"
- "Ingress web UI not accessible"
- "S6 service error"
- "SUPERVISOR_TOKEN not found"
Known Issues Prevention
| Issue | Root Cause | Solution |
|---|---|---|
| Add-on fails to start | Missing or non-executable S6 service files | Create /etc/s6-overlay/s6-rc.d/ with executable run scripts |
| Supervisor API 401 errors | Missing or invalid SUPERVISOR_TOKEN | Token automatically injected - verify permissions in config.yaml |
| Data loss after restart | Saving outside /data/ directory | Always use bashio::addon::config_path or hardcode to /data/ |
| Build fails on architecture | Using unsupported architecture or missing {arch} placeholder | Use only amd64, armv7, aarch64, armhf, i386; use {arch} in image names |
| Ingress returns 502 | Web app not listening or reverse proxy headers missing | Ensure app listens on configured port; set X-Forwarded-* headers |
When to Use
Use This Skill For
- Creating a new Home Assistant add-on from scratch
- Configuring Dockerfile with Home Assistant base images
- Setting up Docker container services for Home Assistant
- Adding S6 overlay service management
- Integrating with Home Assistant Supervisor API
- Building web UIs with ingress reverse proxy
- Configuring multi-architecture Docker builds
- Publishing add-ons to GitHub repositories
- Troubleshooting add-on startup and runtime issues
Don't Use This Skill For
- General Docker concepts (see docker-fundamentals skill instead)
- Home Assistant automation or scripts (use homeassistant-automation skill)
- ESPHome device configuration (use esphome-config-helper skill)
- Kubernetes container orchestration
Quick Usage
Create a basic add-on:
# 1. Create directory structure
mkdir -p my-addon/{rootfs,rootfs/etc/s6-overlay/s6-rc.d/my-service}
# 2. Create config.yaml
cat > my-addon/config.yaml << 'EOF'
name: My Add-On
slug: my-addon
version: 1.0.0
arch: [amd64, armv7, aarch64]
permissions: [homeassistant]
EOF
# 3. Create Dockerfile
cat > my-addon/Dockerfile << 'EOF'
FROM ghcr.io/home-assistant/{arch}-base:latest
COPY rootfs /
CMD ["/init"]
EOF
# 4. Create service script
cat > my-addon/rootfs/etc/s6-overlay/s6-rc.d/my-service/run << 'EOF'
#!/command/execlineb -P
/app/my-service
EOF
chmod +x my-addon/rootfs/etc/s6-overlay/s6-rc.d/my-service/runToken Efficiency
| Approach | Estimated Tokens | Time |
|---|---|---|
| Manual Implementation | 8,000-12,000 | 2-3 hours |
| With This Skill | 2,000-3,000 | 30-45 minutes |
| Savings | 70-75% | 65-80% faster |
Savings from pre-built patterns, configuration templates, troubleshooting guides, and architectural best practices.
File Structure
ha-addon/
├── SKILL.md # Detailed instructions and patterns
├── README.md # This file - discovery and quick reference
├── assets/config.yaml # Template configuration file
├── assets/Dockerfile # Template Dockerfile
├── assets/bashio-reference.md # bashio helper functions reference
└── references/ # Supporting documentation
└── supervisor-api.md # Supervisor API endpoints and examplesDependencies
| Package | Version | Verified |
|---|---|---|
| Home Assistant | 2024.1+ | 2025-12-31 |
| Docker | 20.10+ | 2025-12-31 |
| Docker buildx | Latest | 2025-12-31 |
| S6 Overlay | 3.x | Included in base images |
| bashio | Latest | Included in base images |
All dependencies except Docker come pre-installed in Home Assistant base images.
Official Documentation
- Home Assistant Add-On Development Guide
- Add-On Configuration Reference
- Supervisor API Reference
- bashio Helper Library
- S6 Overlay Documentation
Related Skills
esphome-config-helper- ESP32/ESPHome device integration patternshome-assistant-dashboard- Lovelace dashboard configuration (complementary)frigate-configurator- NVR system setup (works with add-ons)
---
License: MIT
Skill Purpose: Reduce add-on development time and prevent common architectural mistakes with proven patterns and troubleshooting guidance.
Home Assistant Supervisor API Reference
The Supervisor API allows add-ons to communicate with the Home Assistant system, query add-on status, manage other add-ons, and send notifications.
Official Documentation: https://developers.home-assistant.io/docs/supervisor/developing
Quick Start
All API calls:
- Base URL:
http://supervisor - Authentication:
Authorization: Bearer ${SUPERVISOR_TOKEN}header - Content-Type:
application/json - Response format: JSON
The SUPERVISOR_TOKEN environment variable is automatically set by Home Assistant.
Core Endpoints
System Information
Get Supervisor Info
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/infoResponse:
{
"version": "2024.12.0",
"arch": "amd64",
"timezone": "Europe/Amsterdam",
"homeassistant_version": "2024.12.0",
"machine": "raspberrypi4",
"update_available": false
}Get Home Assistant Info
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/homeassistant/infoResponse:
{
"version": "2024.12.0",
"update_available": true,
"machine": "raspberrypi4",
"timezone": "Europe/Amsterdam",
"logging_level": "INFO"
}Add-On Management
Get Current Add-On Info
Retrieve details about the add-on making the request:
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/self/infoResponse:
{
"name": "My Add-On",
"slug": "my-addon",
"version": "1.0.0",
"uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
"state": "started",
"enabled": true,
"options": {
"debug": false,
"log_level": "info"
}
}Get All Add-Ons
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addonsResponse:
{
"addons": [
{
"name": "SSH Add-On",
"slug": "ssh",
"state": "started",
"update_available": false
},
{
"name": "My Add-On",
"slug": "my-addon",
"state": "started",
"update_available": false
}
]
}Get Specific Add-On Info
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/infoStart Add-On
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/startStop Add-On
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/stopRestart Add-On
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/restartGet Add-On Logs
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/logsUpdate Add-On Configuration
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"boot": "auto",
"auto_update": true
}' \
http://supervisor/addons/mysql/optionsAdd-On Lifecycle
Restart This Add-On
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/self/restartStop This Add-On
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/self/stopUpdate This Add-On Configuration
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"boot": "auto",
"auto_update": false,
"options": {
"debug": true,
"log_level": "debug"
}
}' \
http://supervisor/addons/self/optionsHome Assistant API
Get Home Assistant State
Query Home Assistant entity states:
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/homeassistant/api/statesCall Home Assistant Service
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"entity_id": "light.living_room",
"brightness": 255
}' \
http://supervisor/homeassistant/api/services/light/turn_onNotifications
Send Notification
Send a notification to Home Assistant UI:
curl -X POST \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"title": "My Add-On",
"message": "Something important happened",
"notification_id": "my-addon-event-1",
"data": {
"custom_field": "custom_value"
}
}' \
http://supervisor/notifications/createParameters:
title: Notification titlemessage: Notification message bodynotification_id: Unique identifier to prevent duplicatesdata: Optional custom fields
Logs & Debugging
Get Add-On Logs
# Get current add-on logs
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/self/logs
# Get other add-on logs
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/mysql/logsGet Supervisor Logs
curl -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/logsEnvironment Variables
Home Assistant automatically sets these in add-on containers:
| Variable | Value | Example |
|---|---|---|
SUPERVISOR_TOKEN | Authentication token | eyJ0... (JWT) |
SUPERVISOR_HOST | Supervisor hostname | supervisor |
SUPERVISOR_API_ENDPOINT | API base URL | http://supervisor |
Error Handling
API responses use standard HTTP status codes:
# Success (200, 201)
curl -w "\n%{http_code}\n" \
-X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/info
# Common errors:
# 200 OK
# 201 Created
# 400 Bad Request (invalid JSON)
# 401 Unauthorized (missing/invalid token)
# 404 Not Found (add-on doesn't exist)
# 500 Internal Error (supervisor error)Parse Error Response
response=$(curl -s -X GET \
-H "Authorization: Bearer ${SUPERVISOR_TOKEN}" \
http://supervisor/addons/nonexistent/info)
if echo "${response}" | grep -q "error"; then
error=$(echo "${response}" | jq -r '.error')
echo "Error: ${error}"
exit 1
fiReal-World Examples
Monitor Another Add-On
#!/bin/bash
# Check if MySQL is running
TOKEN="${SUPERVISOR_TOKEN}"
ADDON="mysql"
info=$(curl -s -X GET \
-H "Authorization: Bearer ${TOKEN}" \
http://supervisor/addons/${ADDON}/info)
state=$(echo "${info}" | jq -r '.state')
if [ "${state}" = "started" ]; then
echo "MySQL is running"
else
echo "MySQL is ${state}, starting it..."
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
http://supervisor/addons/${ADDON}/start
fiSend Alert on Startup
#!/bin/bash
# Notify user when add-on starts
TOKEN="${SUPERVISOR_TOKEN}"
curl -X POST \
-H "Authorization: Bearer ${TOKEN}" \
-H "Content-Type: application/json" \
-d '{
"title": "My Add-On",
"message": "Started successfully",
"notification_id": "my-addon-started"
}' \
http://supervisor/notifications/createGet System Information
#!/bin/bash
# Log system info on startup
TOKEN="${SUPERVISOR_TOKEN}"
info=$(curl -s -X GET \
-H "Authorization: Bearer ${TOKEN}" \
http://supervisor/info)
version=$(echo "${info}" | jq -r '.version')
machine=$(echo "${info}" | jq -r '.machine')
timezone=$(echo "${info}" | jq -r '.timezone')
echo "Supervisor: ${version}"
echo "Machine: ${machine}"
echo "Timezone: ${timezone}"Restart on Configuration Update
#!/bin/bash
# Watch for configuration changes and restart add-on
TOKEN="${SUPERVISOR_TOKEN}"
ADDON_SLUG="my-addon"
while true; do
sleep 60
info=$(curl -s -X GET \
-H "Authorization: Bearer ${TOKEN}" \
http://supervisor/addons/${ADDON_SLUG}/info)
# Check if configuration changed
# (Implementation depends on your needs)
# Restart if needed
# curl -X POST \
# -H "Authorization: Bearer ${TOKEN}" \
# http://supervisor/addons/${ADDON_SLUG}/restart
doneUsing bashio Helpers
bashio provides wrapper functions around these endpoints:
# Instead of: curl -X GET ... /addons/self/info
bashio::addon::self_info
# Instead of: curl -X POST ... /addons/mysql/restart
bashio::addon::restart "mysql"
# Instead of: curl -X POST ... /notifications/create
bashio::notification::send "Title" "Message"Prefer bashio helpers when available - they handle authentication and parsing automatically.
Permissions Required
Each endpoint requires specific permissions in config.yaml:
permissions:
homeassistant # HA state queries, service calls
hassio # Supervisor API access
admin # System-level operations
backup # Backup operations
manager # Broader add-on managementRate Limiting
- No explicit rate limits documented
- Use reasonable intervals (1+ second between API calls)
- Batch operations when possible
- Cache responses to reduce API calls
Troubleshooting
"401 Unauthorized"
# Verify token is set
echo $SUPERVISOR_TOKEN
# Check permissions in config.yaml
# Add 'hassio' if calling Supervisor API endpoints
permissions:
- hassio"Connection refused"
# Verify hostname (always use 'supervisor')
# Verify you're inside the Docker container
# Add hostname 'supervisor' to add-on Docker network"Invalid JSON response"
# Verify Content-Type header is set
# Verify JSON payload is valid
# Check API response for error messages:
curl -s ... | jq .