Appearance
Metadata Module Application Guide
This guide explains how to use the Metadata module of the Coldwave Cloud Backend from an application developer perspective. It focuses on how to:
- Show connection status and “last seen” information for devices.
- Attach and edit human-readable metadata (names, locations, tags) on devices.
- Use meta schemas to standardize metadata across your fleet.
- Integrate WebSocket events to keep UI state up to date.
The Metadata module does two things: it keeps track of connection-related information (online/offline/idle, last message) and it stores arbitrary key/value data per device. citeturn3view0
When to use metadata (and when not)
Use Metadata for information that describes a device, but is not part of its live measurements or control properties. Typical examples:
- Human-readable device name ("Chiller #1", "Elevator A / Lobby").
- Location hierarchy (site → building → floor → room).
- Customer / project identifiers (e.g. customerId, contractId).
- Installation data (installer, installDate, commissioning notes).
- Tags for grouping and filtering ("demo", "critical", "test", "HVAC").
Do not use metadata for:
- Time series data (measurements over time) → use Timestream / History.
- High-frequency live values → use Service properties or Stream module. citeturn2view0
- Device configuration that must be enforced on the device (keep that on the device or in dedicated config services).
A good mental model:
Service properties describe what the device is doing right now.
Metadata describes what the device is and where it belongs.
Data model in practice
Device-level meta object
For a single device, GET /api/v1/meta/:deviceIdentifier returns its meta information. citeturn5view0
The response includes:
status–"online" | "offline" | "idle".lastMessage– UNIX timestamp (ms) of last message from device.lastConnect– UNIX timestamp (ms) of last connect event.numConnects– number of connect attempts since backend start (non-persistent).data– dictionary of user-defined metadata.address– optional numeric or structured address (depends on deployment).
Example response (simplified):
json
{
"status": "online",
"lastMessage": 1700000000000,
"lastConnect": 1699999900000,
"numConnects": 23,
"data": {
"name": "Chiller #1",
"location": "Building A / Basement",
"customerId": "CUST-4711",
"tags": ["HVAC", "critical"]
}
}In your frontend state model (based on the generic AppState), you can define:
ts
type DeviceId = string;
type DeviceConnectionStatus = "online" | "offline" | "idle";
interface DeviceMeta {
status: DeviceConnectionStatus;
lastMessage?: number;
lastConnect?: number;
numConnects?: number;
address?: number | unknown;
data: Record<string, unknown>;
}
interface Device {
deviceIdentifier: DeviceId;
services: string[];
meta?: DeviceMeta;
// other fields (e.g. from DeviceInfo / Schema)
}
interface AppState {
devices: Record<DeviceId, Device>;
// other maps (services, schemas, alarms, notes, ...)
}Fleet-wide meta list
GET /api/v1/meta returns a list of devices that have meta data. With the depth query parameter, the response includes the full meta information for each device. citeturn5view0
Shape (simplified):
json
[
{
"deviceIdentifier": "DEVICE-1234",
"metaInformation": {
"status": "online",
"lastMessage": 1700000000000,
"data": { "name": "Chiller #1", "location": "Building A" }
}
},
{
"deviceIdentifier": "DEVICE-5678",
"metaInformation": {
"status": "offline",
"lastMessage": 1699900000000,
"data": { "name": "Elevator A", "location": "Lobby" }
}
}
]This endpoint is ideal for bootstrapping device lists that need name, location and status in one call.
Meta schemas: structuring device metadata
Meta schemas let you define a typed schema for metadata keys, similar to service schemas for properties. citeturn3view0
A meta schema has:
- A
name(e.g."default","elevator"). - Optional
deviceType(reserved for future use). - A
propertiesdictionary; each key defines:name– human-readable label.description– optional description.type– one ofnumber | string | boolean | datetime.- Optional
enum– map of allowed values withnameanddescription. - Optional numeric constraints:
rangeMin,rangeMax,step. - Optional
unit– unit string. - Optional
group– used to cluster fields in the UI. - Optional
tag– mark field as “tag”, suitable for quick filters. - Optional
translations– alternate labels per language.
You manage meta schemas via:
GET /api/v1/meta/schemas– list schemas (withdepthto include definitions).POST /api/v1/meta/schemas– create schema.GET /api/v1/meta/schemas/:name– get one schema.PATCH /api/v1/meta/schemas/:name– update/merge schema.DELETE /api/v1/meta/schemas– delete all schemas.DELETE /api/v1/meta/schemas/:name– delete one schema. citeturn5view1
Example meta schema
json
{
"name": "default",
"default": true,
"properties": {
"name": {
"name": "Device name",
"description": "Human-readable device name",
"type": "string",
"group": "General"
},
"location": {
"name": "Location",
"description": "Site / building / room",
"type": "string",
"group": "General"
},
"installDate": {
"name": "Installation date",
"type": "datetime",
"group": "Lifecycle"
},
"customerId": {
"name": "Customer ID",
"type": "string",
"group": "Commercial"
},
"criticality": {
"name": "Criticality",
"type": "number",
"enum": {
"1": { "name": "Low" },
"2": { "name": "Medium" },
"3": { "name": "High" }
},
"group": "Operational"
},
"tags": {
"name": "Tags",
"description": "Tags used for search and filters",
"type": "string",
"tag": true,
"group": "General"
}
}
}In a configuration or admin UI, you can build a form generator on top of such schemas:
type: "string"→ text input.type: "number"+rangeMin/rangeMax/step→ numeric input / slider.type: "datetime"→ date/time picker.enumdefined → select/dropdown.tag: true→ tag chips / multi-select for filters.
UX patterns for metadata in applications
Common patterns across UIs:
- Device lists / fleet overview
- Show name, location, status, and important tags from metadata.
- Allow filtering by tags, location, status.
- Device detail header
- Display name, location, status badge (online/offline/idle), last seen.
- Optionally show customer info, install date, criticality.
- Search / filters
- Search device list by
name,location,customerId. - Filter by tag-like fields where
tag: truein the meta schema.
- Search device list by
- Edit metadata dialog
- Form generated from meta schema: simple key/value editing.
- Use PATCH updates for small changes (see below).
Metadata is highly user-facing; keep labels and descriptions translated via the lang parameter on /meta/schemas where needed. citeturn3view0
API usage from a frontend
1. Bootstrapping devices with metadata
Typical initial sequence (complementing DeviceInfo/Service calls):
Load devices/services (e.g. via Service/DeviceInfo APIs).
Load metadata for all devices:
httpGET /api/v1/meta?depth=1 Authorization: Bearer <token>Merge
metaInformationinto yourDeviceobjects.Optionally load
GET /api/v1/meta/schemas?depth=1&lang=ento drive UI forms.
Example merge logic (TypeScript-style pseudocode):
ts
function applyMetaListToState(state: AppState, response: any[]) {
for (const entry of response) {
const deviceId: DeviceId = entry.deviceIdentifier;
if (!state.devices[deviceId]) {
state.devices[deviceId] = { deviceIdentifier: deviceId, services: [] };
}
const info = entry.metaInformation ?? {};
state.devices[deviceId].meta = {
status: info.status ?? "offline",
lastMessage: info.lastMessage,
lastConnect: info.lastConnect,
numConnects: info.numConnects,
address: info.address,
data: info.data ?? {},
};
}
}2. Reading metadata for a single device
Use this for the device detail page or when the list was not bootstrapped:
http
GET /api/v1/meta/:deviceIdentifier
Authorization: Bearer <token>- Requires
readpermission for themetaresource. citeturn5view0 - Response includes
status, timestamps,numConnects,data, and optionaladdress.
3. Setting metadata (overwrite)
To completely replace the data object for a device, use:
http
PUT /api/v1/meta/:deviceIdentifier
Authorization: Bearer <token>
Content-Type: application/json
{
"name": "Chiller #1",
"location": "Building A / Basement",
"customerId": "CUST-4711"
}- Requires
updatepermission formeta. citeturn5view0 - The request body becomes the new
dataobject; previous keys are removed. - Sending
{}deletes all user-defined metadata.
Use this in admin tools or when importing / synchronizing metadata from another system.
4. Updating metadata (merge)
To change only specific keys, use:
http
PATCH /api/v1/meta/:deviceIdentifier
Authorization: Bearer <token>
Content-Type: application/json
{
"location": "Building B / Floor 2",
"criticality": 3
}- Requires
updatepermission formeta. citeturn5view0 - Backend merges the request body with the existing
dataobject; overlapping keys are overwritten.
To delete a single key without overwriting everything, set it to __DELETE in your PATCH body: citeturn5view0
json
{
"customerId": "__DELETE"
}This will remove the customerId key from the data object.
5. Deleting metadata
To remove all metadata for a device (not just individual keys):
http
DELETE /api/v1/meta/:deviceIdentifier
Authorization: Bearer <token>- Requires
deletepermission formeta. citeturn5view0 - After deletion,
GET /api/v1/meta/:deviceIdentifierwill only contain status fields (status/lastMessage/lastConnect/numConnects/address) until new data is set.
Connection status and “last seen” in the UI
The metadata module automatically tracks the connection status for each device, based on incoming messages and backend pings: citeturn3view0
online– device sends messages or answers pings.offline– no messages for a while.idle– ping unanswered; device is considered idle.
Timestamps:
lastMessage– last message from device (ms since epoch).lastConnect– last connection event.numConnects– total connect attempts in backend lifetime.
UI patterns:
- Show a colored badge (green/yellow/grey) for status on lists and detail views.
- Show “Last message X minutes ago” based on
lastMessage. - Optionally highlight devices that have been offline for more than N minutes/hours.
Example derived fields:
ts
function buildConnectionInfo(meta?: DeviceMeta) {
if (!meta) {
return { status: "unknown", lastSeenText: "No data" };
}
const now = Date.now();
const lastSeenTs = meta.lastMessage ?? meta.lastConnect;
const deltaMinutes = lastSeenTs
? Math.round((now - lastSeenTs) / 60000)
: undefined;
const lastSeenText = lastSeenTs
? `${deltaMinutes} min ago`
: "No recent data";
return {
status: meta.status,
lastSeenText,
};
}WebSocket integration
The Metadata module emits the following events on the WebSocket: citeturn3view0
META_STATUS_CHANGEMETA_SETMETA_DELETEMETA_SCHEMA_ADDMETA_SCHEMA_UPDATEMETA_SCHEMA_DELETE
The documentation currently details the payload for META_STATUS_CHANGE. It includes:
type–"META_STATUS_CHANGE".deviceIdentifier– affected device ID.status–online | offline | idle.lastMessage,lastConnect,numConnects, optionaladdress. citeturn3view0
In your frontend, you can handle it like this (TypeScript-style pseudocode):
ts
function applyMetaEvent(state: AppState, event: any) {
const deviceId: DeviceId = event.deviceIdentifier;
if (!state.devices[deviceId]) {
state.devices[deviceId] = { deviceIdentifier: deviceId, services: [] };
}
const dev = state.devices[deviceId];
dev.meta ??= { status: "offline", data: {} };
switch (event.type) {
case "META_STATUS_CHANGE": {
dev.meta.status = event.status;
if (event.lastMessage != null) dev.meta.lastMessage = event.lastMessage;
if (event.lastConnect != null) dev.meta.lastConnect = event.lastConnect;
if (event.numConnects != null) dev.meta.numConnects = event.numConnects;
if (event.address != null) dev.meta.address = event.address;
break;
}
case "META_SET":
case "META_DELETE": {
// safest strategy: re-fetch this device's meta
// GET /api/v1/meta/:deviceIdentifier
// and merge as in applyMetaListToState
break;
}
case "META_SCHEMA_ADD":
case "META_SCHEMA_UPDATE":
case "META_SCHEMA_DELETE": {
// For most applications, these events matter only in admin/config UIs.
// You can re-fetch /api/v1/meta/schemas?depth=1 if needed.
break;
}
}
}This keeps your main UI logic simple and delegates any ambiguity in META_SET/DELETE payloads to the backend via fresh GET requests.
Permissions and multi-tenant behaviour
Permissions for Metadata are controlled by the resources: citeturn3view0
meta.schemas– manage meta schemas (create/read/update/delete).meta– read and modify per-device metadata.
Common patterns:
- Viewer / Operator
- Needs
readonmetato see names, locations, status. - No write permission → hide or disable edit controls.
- Needs
- Admin / Integrator
updateonmetafor editing device metadata.create/update/deleteonmeta.schemasif they can manage schemas.
In multi-tenant environments, device access control applies automatically:
- Users only see metadata for devices they are allowed to access.
- No additional tenant field is necessary in metadata itself.
Summary
- The Metadata module provides connection status and per-device key/value data.
- Use it for names, locations, tags and other descriptive information – not for live measurements or history.
- Use
GET /api/v1/metawithdepthto bootstrap your device list with status and names, andGET/PUT/PATCH/DELETE /api/v1/meta/:deviceIdentifierto manage individual devices. - Define meta schemas to standardize fields and drive dynamic forms and filters.
- Subscribe to WebSocket events (
META_STATUS_CHANGE, etc.) to keep your UI in sync.
With these patterns, you can build device lists and detail pages that feel consistent and informative, while keeping the domain model cleanly separated between live data and static metadata.