An addon can ship and install a custom integration into Home Assistant. This gives the addon a native HA presence: config flow, sensors, and event handling, without users manually copying files.
Benefits
Unified experience. The addon and integration share one install. Users add the addon; the integration appears as an option in Devices & services. No separate HACS or manual custom_components setup.
Event and service access. The integration runs inside HA core. It can listen to hass.bus events (e.g. mobile_app_notification_action for notification taps), call services, and use coordinators. The addon stays a simple API server; the integration handles HA-specific wiring. This avoids requiring users to create automations for common flows.
Version alignment. The integration is bundled in the addon image. When the addon updates, the integration is overwritten on next start. Users always get the integration version that matches the addon.
Single configuration. Users configure the addon URL once in the integration config flow. The integration uses that URL for all addon API calls.
Setup: Auto Install on Addon Start
Add an init script that runs before your main service. Use the S6 overlay so this script completes before the API starts.
- Add
homeassistant_config:rwto the addonmapinconfig.yaml:
map:
- addon_config:rw
- homeassistant_config:rw
- Bundle the integration in the addon image. In your Dockerfile, copy the integration source into the image:
COPY integration/ /app/integration/
- Create an init script (e.g.
rootfs/etc/s6-overlay/s6-rc.d/init-coned/run):
#!/command/with-contenv bashio
INTEGRATION_SRC="/app/integration/coned_connect"
INTEGRATION_DST="/homeassistant/custom_components/coned_connect"
if [ -d "$INTEGRATION_SRC" ]; then
bashio::log.info "Installing integration..."
mkdir -p /homeassistant/custom_components
rm -rf "$INTEGRATION_DST"
cp -r "$INTEGRATION_SRC" "$INTEGRATION_DST"
bashio::log.info "Integration installed to $INTEGRATION_DST"
fi
-
Order services so init runs first. The init service must complete before the API. In S6, use an
upfile that lists dependencies so the API service waits for init. -
Make the script executable in the Dockerfile:
RUN chmod +x /etc/s6-overlay/s6-rc.d/init-coned/run
Each addon start: rm -rf plus cp -r ensures a clean copy. No stale files from previous versions.
On Addon Removal
The Supervisor does not run addon code when an addon is uninstalled. The integration files in custom_components are not deleted automatically. They remain until the user removes the integration from Devices & services and deletes custom_components/<integration_name>, or until another addon overwrites that path.
Users who uninstall the addon should remove the integration from Devices & services. The integration will fail to connect anyway once the addon is gone.
Integration Design
The integration should:
-
Use a config flow that asks for the addon URL (e.g.
http://xxx-addon-slug:8000or the internal Docker hostname). -
Validate the URL by calling an addon health/version endpoint.
-
Poll the addon API (coordinator) for sensor data.
-
Register event listeners when needed (e.g. for notification actions) and forward events to the addon API.
-
Use
call_soon_threadsafewhen scheduling work from event callbacks that may run off the main loop.
Place the integration in a folder like integration/<domain>/ in the addon repo, mirroring the standard custom_components/<domain>/ layout (manifest.json, init.py, config_flow.py, etc.).