Device templates
A device template is a global platform catalogue entry that tells Voke how to decode device wire data into named signals and actions.
What is a device template?
A device template is a reusable decoder specification. It maps raw wire data — bitmask integers for cabinets, named field maps for meters, inverters, and batteries — to a set of named signals (telemetry values) and named actions (legal commands). Once assigned to a plant or sub-device, the template governs how the telemetry pipeline decodes and stores every incoming snapshot entry.
Templates are global platform-controlled contracts. Every organization reads the same catalogue; only SuperAdmins can create, edit, or delete templates.
level
Every template is either a plant-level decoder or a sub-device-level decoder. The distinction is expressed by DeviceTemplateLevel (from @cpi/shared/types/enums):
export enum DeviceTemplateLevel {
PLANT = 'PLANT',
SUB_DEVICE = 'SUB_DEVICE',
}| Level | Assigned to |
|---|---|
PLANT | A plant via plants.templateId |
SUB_DEVICE | A sub-device via sub_devices.templateId |
level is immutable. Once a template is created, its level cannot be changed because UpdateDeviceTemplateDto intentionally omits it. To use a different level, create a new template or clone the existing one.
Structure
A template has a name, a type, a version, and two arrays:
| Field | Type | Description |
|---|---|---|
name | string (max 120) | Human-readable identifier. Together with version, forms a global unique index. |
type | SubDeviceType | The physical kind this template decodes (CABINET, METER, INVERTER, or BATTERY). |
version | integer | Auto-incremented when signals are edited. Actions changes do not bump version (actions are command vocabulary, not a stored telemetry schema). |
signals | SubDeviceSignal[] | Array of up to 256 signal definitions — the telemetry decoder. See Signals. |
actions | SubDeviceAction[] | Array of up to 64 action definitions — the command vocabulary. See Commands & alerts. |
isSystem | boolean | Set by Voke for built-in preset templates. See below. |
Signal keys must be unique across a template. For binary signals, bit positions must also be unique. Duplicate keys or bit positions are rejected at write-time by the Zod schema (SubDeviceSignalsSchema).
Display metadata
Templates also drive the plant-detail UI:
| Field | Applies to | Purpose |
|---|---|---|
featured | Binary and numeric signals | Signals marked true appear in the compact Devices row strip. If no signal is featured, the row falls back to the first five numeric signals. |
chartGroup | Numeric signals only | Groups numeric signals into Live tab chart panels. Signals with the same chartGroup on the same sub-device render together. |
chartAxis | Numeric signals only | Chooses the chart axis for a numeric signal: left or right. Defaults to left. |
chartGroup and chartAxis are rejected on binary signals because BinarySignalSchema is strict. Binary signals are still valid for featured row chips and alerting, but they are not charted on the Live tab.
Global catalog
Device templates are no longer tenant-owned. The device_templates.organization_id column was removed by migration 1777960000000-DeviceTemplatesGoToGlobal.
Reads are available to authenticated users from every organization. Writes are restricted to SuperAdmins and audited with DEVICE_TEMPLATE_CREATED, DEVICE_TEMPLATE_UPDATED, and DEVICE_TEMPLATE_DELETED security events.
Template assignment on plants and sub-devices now validates semantic fit only:
- Plant assignment requires a
PLANT-level template. - Sub-device assignment requires a matching
SubDeviceType. - Missing template IDs still return
DEVICE_TEMPLATE_NOT_FOUND.
System templates
Some templates ship with Voke as system presets (isSystem: true). System templates:
- Cannot be edited. Attempting to update a system template returns
DEVICE_TEMPLATE_SYSTEM_READONLY. - Cannot be deleted. Attempting to delete a system template returns
DEVICE_TEMPLATE_SYSTEM_READONLY. - Are visible to every organization, like any other template in the global catalogue.
To customize a system template, clone it in the admin UI (DeviceTemplateEditor → Clone). The clone is org-owned and fully editable.
Validation error codes
| Code | When |
|---|---|
DEVICE_TEMPLATE_NOT_FOUND | Template does not exist. |
DEVICE_TEMPLATE_WRONG_LEVEL | Assigning a SUB_DEVICE-level template to a plant. The device-template update endpoint does not accept level changes. |
DEVICE_TEMPLATE_SYSTEM_READONLY | Attempting to edit or delete a system template. |
DEVICE_TEMPLATE_SIGNAL_INVALID | signals array fails Zod validation (duplicate key, invalid bit, unknown field, etc.). |
DEVICE_TEMPLATE_ACTION_INVALID | actions array fails Zod validation (duplicate key, invalid param type, etc.). |
DEVICE_TEMPLATE_IN_USE | Attempting to delete a template that is still referenced by one or more sub-devices. |
CONFLICT | Creating a template with the same (name, version) pair as an existing one. |
See also
- Signals — the
signals[]array in detail: binary vs. numeric, storage model,featuredflag - Commands & alerts — the
actions[]array and how commands are validated against it - Plants —
PLANT-level template assignment and validation - Sub-devices —
SUB_DEVICE-level template assignment and validation
Sub-devices
Sub-devices model the physical components — cabinets, meters, inverters, batteries — that live under a plant. They are never MQTT participants; all their telemetry arrives through the plant.
Signals
Signals are the named telemetry values decoded from device payloads. Two kinds exist — binary (bitmask bits) and numeric (named fields) — with different storage strategies.