Skip to main content

Plugins Overview

Plugins are separate processes that bridge physical or cloud devices to HomeCore via MQTT. They run independently — crashing or restarting a plugin never affects HomeCore itself.

Architecture

Physical device (Zigbee, Z-Wave, WiFi, cloud API)
↕ native protocol
Plugin process
↕ MQTT
HomeCore embedded broker (port 1883)
↕ internal event bus
HomeCore state bridge → rule engine

Each plugin:

  1. Connects to the embedded MQTT broker with its own plugin_id credential
  2. Publishes device discovery via homecore/plugins/{plugin_id}/register
  3. Publishes device state to homecore/devices/{device_id}/state (retained)
  4. Publishes availability to homecore/devices/{device_id}/availability (retained)
  5. Subscribes to homecore/devices/{device_id}/cmd for commands from HomeCore

Plugin isolation: The SDK subscribes to specific device command topics (homecore/devices/{device_id}/cmd) rather than using wildcards. This prevents cross-plugin command contamination — a plugin only receives commands for devices it owns. Subscriptions are tracked internally and restored automatically on MQTT reconnect.

HomeCore treats the plugin as the source of truth for device discovery. In normal operation you do not pre-create devices manually. When the plugin discovers a light, sensor, speaker, or scene, it registers that device and HomeCore adds it to the registry automatically.

Plugins can also declare capability actions — plugin-specific commands like "Pair Hue bridge", "Include Z-Wave device", "Rescan devices". These show up as buttons under Actions on the plugin's detail page in the admin UI and are exposable to MCP clients without plugin-specific code. See Plugin Capabilities & Actions.

Available plugins

PluginLanguageDevices
hc-hueRustPhilips Hue lights, groups, scenes
hc-yolinkRustYoLink sensors, door sensors, outlets
hc-lutronRustLutron RadioRA2 dimmers, switches, scenes
hc-sonosRustSonos speakers, transport controls, favorites, playlists
hc-zwaveRustZ-Wave devices via zwave-js WebSocket
hc-wledRustWLED LED controllers
hc-isyRustISY/IoX hub (Insteon, Z-Wave, Zigbee)
hc-thermostatRustVirtual thermostats (aggregated sensors → actuator)
http-pollerRustGeneric HTTP endpoint polling

Plugin configuration

All plugins share the same [homecore] config section:

# config/config.toml (in the plugin's directory)

[homecore]
broker_host = "127.0.0.1"
broker_port = 1883
plugin_id = "plugin.hue"
password = "" # set if broker auth is enabled

The config file path defaults to config/config.toml relative to the binary's working directory. Override with the first CLI argument:

./hc-hue /etc/homecore-plugins/hue.toml

Running plugins

Plugins are independent binaries. Run them from their installation directory:

cd /path/to/hc-hue
./hc-hue config/config.toml

Or with run-dev.sh for local development (relative paths from the workspace root):

./scripts/run-dev.sh   # starts HomeCore + all configured plugins

Plugin device registration

Plugins register devices by publishing a JSON payload to homecore/plugins/{plugin_id}/register.

For well-known device classes, the recommended path is typed registration. The plugin sends a canonical device_type, and HomeCore resolves the built-in schema automatically. This is how different plugins can expose lights, switches, and now media players in a consistent way.

Typed registration example

{
"device_id": "sonos_living_room",
"plugin_id": "plugin.sonos",
"name": "Living Room",
"area": "living_room",
"device_type": "media_player"
}

When HomeCore receives that registration it:

  1. Creates or updates the device record
  2. Stores the device_type
  3. Resolves the matching built-in device schema
  4. Exposes the device to the API, UI, and rule engine without manual setup

Explicit capabilities example

Plugins can also publish an explicit schema when they need a custom device shape:

{
"device_id": "hue_001788fffe6841b3_1",
"plugin_id": "plugin.hue",
"name": "Living Room Main",
"area": "living_room",
"device_type": "light",
"capabilities": {
"on": {"type": "boolean"},
"brightness": {"type": "integer", "minimum": 0, "maximum": 254},
"color_temp": {"type": "integer", "minimum": 2000, "maximum": 6500}
}
}

Use explicit capabilities when the device does not fit a built-in type cleanly. Use device_type when you want HomeCore to understand the device as a known category.

Known device types

The built-in catalog includes common categories such as:

  • switch
  • light
  • lock
  • cover
  • climate
  • binary_sensor
  • temperature_sensor
  • media_player

This matters because HomeCore can now treat media players from different plugins the same way at the automation layer. A plugin such as hc-sonos can register speakers as media_player, and rules can target those players by HomeCore device ID instead of plugin-local URLs or IP addresses.

HomeCore stores the device in the registry. Subsequent registrations on plugin restart are upserts, so names, areas, types, and schemas stay in sync with what the plugin reports.

Plugin startup race condition

Problem: If a plugin starts very quickly and HomeCore's internal MQTT client isn't subscribed to homecore/# yet, the registration message is missed.

Solution: Set plugin_ready_delay_secs in homecore.toml (default: 10 seconds). HomeCore waits this many seconds after startup before the mode manager publishes initial states. The MQTT client subscription is established well before plugins connect.

For automated scripts, wait for HomeCore to print API server starting before launching plugins.

Connecting to an external broker

For multi-machine deployments, configure plugins to connect to the same external broker as HomeCore:

[homecore]
broker_host = "192.168.1.10" # HomeCore machine IP
broker_port = 1883
plugin_id = "plugin.hue"
password = "hue-password"

Plugin management protocol

HomeCore includes a PluginManager that supervises managed plugins. Each managed plugin runs under a per-plugin supervisor task with start/stop/restart support and exponential backoff on crashes.

Management MQTT topics

DirectionTopicPurpose
Plugin → HChomecore/plugins/{id}/heartbeatPlugin liveness signal (30-60s interval)
HC → Pluginhomecore/plugins/{id}/manage/cmdManagement commands from core
Plugin → HChomecore/plugins/{id}/manage/responsePlugin response to management commands

Management commands

CommandDescription
pingHealth check — plugin responds with pong
get_configRequest plugin's current configuration
set_configPush configuration changes to the plugin
set_log_levelChange the plugin's log level at runtime

Heartbeat and timeout

Managed plugins publish a heartbeat message every 30-60 seconds. The PluginManager runs a timeout sweep and marks a plugin as offline after 90 seconds without a heartbeat.

Management API

EndpointDescription
GET /api/v1/plugins/:idGet plugin status and details
PATCH /api/v1/plugins/:idUpdate plugin metadata
POST /api/v1/plugins/:id/startStart a managed plugin
POST /api/v1/plugins/:id/stopStop a managed plugin
POST /api/v1/plugins/:id/restartRestart a managed plugin
GET /api/v1/plugins/:id/configGet plugin configuration
PUT /api/v1/plugins/:id/configUpdate plugin configuration
DELETE /api/v1/plugins/:id/devicesBulk-wipe every device the plugin owns; plugin re-registers live ones on next sync. See Devices → Wipe all devices for one plugin.

Managed plugins

All Rust device plugins use the official SDK with full management protocol support:

  • hc-hue — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-wled — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-yolink — + custom action: rescan_devices
  • hc-lutron — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-sonos — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-isy — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-zwave — heartbeat, remote config, dynamic log level, MQTT log forwarding
  • hc-thermostat — + custom actions: recalculate_all, reload_config, add_thermostat, remove_thermostat, get_thermostats. First plugin to use the SDK's cross-device state subscription (subscribe_state / run_managed_with_state).

Plugin MQTT topics (reference)

DirectionTopicPurpose
Plugin → HChomecore/devices/{id}/stateFull state update (retained)
Plugin → HChomecore/devices/{id}/state/partialPartial update (JSON merge-patch)
Plugin → HChomecore/devices/{id}/availability"online" or "offline" (retained)
Plugin → HChomecore/plugins/{id}/registerDevice registration
HC → Pluginhomecore/devices/{id}/cmdCommand from HomeCore/API
Plugin → HChomecore/plugins/{id}/heartbeatLiveness signal (managed plugins)
HC → Pluginhomecore/plugins/{id}/manage/cmdManagement command (managed plugins)
Plugin → HChomecore/plugins/{id}/manage/responseManagement response (managed plugins)
Plugin → HChomecore/plugins/{id}/logsForwarded plugin log lines (JSON LogLine)