Overview

Plugins

Widgets

Reference

Publishing

Datasources

A datasource is a named measurement point. Every piece of telemetry your plugin publishes is recorded against a datasource, and every widget or automation condition that reads live or historical data references one.

How datasources work

Datasources live in a central registry keyed by a unique dotted-path string (e.g. tapo.plug.current_power). The registry is the single source of truth for what data the system knows about — widgets, automations, and the telemetry storage layer all reference datasources by this key or by the UUID assigned at registration.

When a datasource is deleted — for example when its plugin is uninstalled — all associated telemetry, rollup data, and event records are automatically removed via cascade. Datasources are preserved across plugin updates because the plugin row is updated in place, keeping its ID and all downstream references intact.

Datasource types

The type field controls how Hiclaro stores and routes the data.number datasources write to the telemetry table (float, with hourly rollups).string and boolean datasources write to the event telemetry table (varchar, deduplicated, no rollup).

TypeDescription
number

A numeric value. Hiclaro stores every sample and computes hourly rollups (avg / min / max) for efficient long-range queries. Use this for anything you want to chart historically — temperature, power draw, state of charge, remaining capacity.

string

A text value representing a mode or status — for example a mode selector (eco / comfort / boost) or a status string (charging / idle). Consecutive identical values are deduplicated; only transitions are stored.

boolean

A true/false toggle — on/off, active/inactive, is_charging. Stored as the strings "true" or "false". Like string, consecutive duplicates are deduplicated.

Key naming conventions

Datasource keys use a dotted-path format: plugin_key.category.metric. The plugin key prefix namespaces your datasources and prevents collisions with other plugins.

// Good — clear namespace and hierarchy
"tapo.plug.current_power"
"tapo.sensor.temperature"
"victron.battery.state_of_charge"
"ha.sensor.bedroom_temperature"

// Avoid — no namespace, ambiguous on a multi-plugin system
"temperature"
"power"

Keep keys stable across plugin versions. Changing a key in an update orphans any widgets or automations that referenced the old key, and leaves stale datasource rows behind.

Choosing a registration approach

There are two ways to register datasources — the plugin.json manifest and the runtime MQTT topic. They serve different use cases and are not meant to be combined. Pick one approach per datasource; using both for the same key is redundant and can cause unexpected double-upserts on startup.

ApproachWhen to useExample
plugin.json manifest

Datasource keys are fixed and known at build time — the plugin always exposes the same set of measurements regardless of what devices are connected.

A Tapo plug plugin that always exposes tapo.plug.current_power and tapo.plug.voltage.

Runtime MQTT

Datasource keys are only known after the service starts and discovers entities — the set varies per installation.

A Home Assistant bridge that registers one datasource per HA entity as they are discovered over the WebSocket API.

If your plugin targets a fixed set of devices with a predictable schema, use the manifest — it is simpler and the datasources are registered at install time before your service even starts. Use runtime MQTT only when the datasource set is genuinely dynamic.

Registration: manifest (static)

The simplest way to register datasources is to declare them in plugin.json. Hiclaro upserts them by key at install time and on every reinstall. Use this approach when the full set of datasources your plugin will ever publish is known at build time.

{
  "key": "my-plugin",
  "datasources": [
    {
      "key": "my-plugin.device.temperature",
      "label": "Device: Temperature",
      "unit": "°C",
      "type": "number"
    },
    {
      "key": "my-plugin.device.status",
      "label": "Device: Status",
      "type": "string"
    },
    {
      "key": "my-plugin.device.battery_soc",
      "label": "Device: Battery SOC",
      "unit": "%",
      "type": "number",
      "meta": { "min": 0, "max": 100 }
    }
  ]
}

See the Plugin Manifest reference for the full field list.

Registration: runtime MQTT (dynamic)

Some plugins integrate platforms — like Home Assistant or Zigbee — where the set of entities is not known until the service connects and discovers them at runtime. For these cases, publish to eims/registry/datasource/register to register datasources on demand.

The API upserts each entry by key, so re-publishing the same registration on reconnect is safe and has no side effects. The registration message also links each datasource to the publishing plugin, so they are cleaned up automatically if the plugin is uninstalled.

// Topic: eims/registry/datasource/register
{
  "pluginKey": "ha",
  "datasources": [
    {
      "key": "ha.sensor.bedroom_temperature",
      "label": "Bedroom Temperature",
      "unit": "°C",
      "type": "number"
    },
    {
      "key": "ha.switch.living_room_light",
      "label": "Living Room Light",
      "type": "string"
    }
  ]
}

Batching and throttling

When a plugin first connects it may discover many entities in a short burst. Rather than sending one MQTT message per entity, buffer new registrations and flush them in batches every few seconds — or immediately when the buffer reaches a fixed size. This keeps MQTT traffic low during startup and avoids hammering the API.

Maintain an in-memory set of already-registered keys so you skip the registration step for subsequent telemetry events from the same entity. Clear the set on reconnect so datasources are re-registered after a restart.

Publishing telemetry

Once a datasource is registered, publish measurements to eims/telemetry. Always use the value field — pass a number for number datasources, or a string for string and boolean datasources.

// Topic: eims/telemetry

// number datasource — value is a float or integer
{
  "entries": [
    { "datasourceKey": "my-plugin.device.temperature", "value": 23.4 },
    { "datasourceKey": "my-plugin.device.battery_soc", "value": 87 }
  ]
}

// string / boolean datasource — value is always a string
{
  "entries": [
    { "datasourceKey": "my-plugin.device.status", "value": "charging" }
  ]
}

// Device-scoped: marks the device online and associates telemetry with it
{
  "deviceAddress": "192.168.1.42",
  "entries": [
    { "datasourceKey": "my-plugin.device.temperature", "value": 23.4 }
  ]
}

// Backdated entry (e.g. replaying historical data)
{
  "entries": [
    {
      "datasourceKey": "my-plugin.device.temperature",
      "value": 21.1,
      "recordedAt": "2025-06-01T12:00:00Z"
    }
  ]
}

Omit deviceAddress for global datasources that are not scoped to a specific device — for example aggregated battery totals or cloud-fetched readings. Telemetry published before a datasource is registered is silently dropped, so always register first.

The meta field

The optional meta object carries supplementary information that widgets can use for rendering. Currently recognised keys:

KeyTypeDescription
min

number

Minimum expected value. Used by gauge and radial bar widgets to set the scale floor.

max

number

Maximum expected value. Used by gauge and radial bar widgets to set the scale ceiling.

© 2026 Hiclaro. All rights reserved.