Appearance
Service Module – Application Guide
This guide explains how application developers can use the Service module of the Coldwave Backend. It focuses on how to:
- Discover devices and their services
- Read current property values
- Change properties and trigger custom messages
- Handle updates via Websocket events
- Combine Service with Schema, Metadata, Streams and Timestream
1. Role of the Service module
The Service module is the current state API for devices. It gives you a live-ish view of each device:
- Which services a device exposes
- Which properties exist on each service
- What the current values of those properties are
- How to change properties
- How to invoke custom messages on a service
- How to work with open properties (stream-like payloads)
In the bigger picture:
| Module | What it gives you |
|---|---|
| Service | Current property values and commands |
| Schema | Human-readable names, enums, units, UI hints |
| Metadata | Device identity, tags, location, ownership |
| Timestream | Historical values and trends |
| Streams | Live updates over Websocket for selected properties |
| Alarms | Rules that react to property values and changes |
Most UI screens will read from Service, enrich with Schema, and combine with Metadata.
2. Core concepts
2.1 Devices, services, properties
- Device – logical or physical unit, identified by
deviceIdentifier - Service – interface a device exposes (
serviceIdentifier), e.g.lift.status,energy.meter - Property – individual value on a service (identified by numeric ID and type, plus schema name)
The Service API works in two representations:
- Raw format (
raw=true): machine-oriented, keyed by numericpropIdand includes modifiers - Parsed format (
raw=false, default): dictionary keyed by schema names (or0x000:TYPEif no schema)
2.2 Property modifiers and tags
In raw mode, each property has:
propId– numeric identifiertype– backend type (INT32,FLOAT,BOOL,STRING, …)value– current value (if present)modifierflags, for example:isReadOnly– cannot be writtenisActionable– “command-like” property requiring acknowledgementisVolatile– value might not be persistedisError– last operation failedisMeta– meta property (e.g. timestamps)isNull– property exists but has no payload yet
The Service API may also tag properties (e.g. tag: "value"). Use these fields to decide how to render values and how to handle errors.
3. Discovering devices and services
The following endpoints help you build navigation in your UI.
3.1 Get all services
http
GET /api/v1/services- Requires
readpermission onservice. - Optional query:
raw– raw vs parsed format (default:false)hex– property IDs as hex strings (default:false)depth– how much nested data to include (properties per device, etc.)
Typical use cases:
- Build a service catalog (“which device types do we have?”)
- Offer filters like “show all devices with
lift.statusservice”
3.2 Get all devices
http
GET /api/v1/devices- Same query parameters as
/services(raw,hex,depth). - Returns all known devices plus, optionally, their services and properties.
Typical use cases:
- Device list view in the UI
- Tying devices to tenants or locations (with help from Metadata)
3.3 Get devices for a specific service
http
GET /api/v1/services/:serviceIdentifier/devicesserviceIdentifier– service name/ID (e.g.lift.status)- Optional
raw,hex,depth
Use this when you build a service-centric UI, for example:
- “Show all lifts that expose
lift.status” - “Show all energy meters with service
energy.meter”
3.4 Get services for a specific device
http
GET /api/v1/devices/:deviceIdentifier/servicesdeviceIdentifier– device ID- Optional
raw,hex,depth
Use this for device-centric UIs, for example:
- Device detail page: list all services of a given device
- Nested tabs:
Status,Alarms,Energy, … per device
4. Reading current properties
The standard pattern is “one device + one service → set of properties”.
4.1 Get properties for a device/service pair
Two equivalent variants exist:
http
GET /api/v1/services/:serviceIdentifier/devices/:deviceIdentifier
GET /api/v1/devices/:deviceIdentifier/services/:serviceIdentifier- Requires
readpermission onservice. - Same query parameters:
raw,hex,depth.
Recommended usage for UI code:
- Use
raw=falseandhex=falseto get parsed, schema-based dictionaries where keys are property names. - Use
raw=truewhen you need low-levelpropId, modifiers, and type information.
4.2 Joining with schema to build UI models
For each service instance, you typically combine:
- Service response (current values) from the Service module
- Property metadata (names, enums, groups) from the Schema module
Basic pattern:
- Call
GET /api/v1/devices/:device/services/:service?raw=false - Look up
ServiceSchemaforserviceIdentifier - For each schema property:
- Find the corresponding value in the Service response (by schema name)
- Create a
UiPropertythat contains id, label, type, value, readonly, enum options, group, etc.
- Group and render properties accordingly
This is the core of a generic “service property panel” in the UI.
5. Updating properties
To change properties, use the Update Properties endpoints:
http
PUT /api/v1/services/:serviceIdentifier/devices/:deviceIdentifier
PUT /api/v1/devices/:deviceIdentifier/services/:serviceIdentifier- Requires
updatepermission onservice.properties. - The request body follows the same property encoding conventions as the read endpoints (raw vs parsed, IDs vs names). In practice, you:
- Decide if you want to address properties by typed ID (
0x2001.UINT16) or by **schema name` - Send a JSON object that includes the new values for those properties
- Decide if you want to address properties by typed ID (
Application-level patterns:
- Wrap the PUT into a helper, e.g.
updateProperties(deviceId, serviceId, payload) - Let schema drive validation (types, enums, readonly)
- Handle failures via HTTP status and
isErrorflags (see Websocket events)
For actionable properties (isActionable=true), treat the write like triggering a command:
- Set desired value
- Wait for remote confirmation (e.g. via OBJECT_UPDATED event)
- Reflect success/failure in the UI (spinner → success/error state)
6. Custom messages
Some services define custom messages (RPC-like operations) that are not just simple property writes.
Endpoints:
http
PUT /api/v1/services/:serviceIdentifier/devices/:deviceIdentifier/customMessage/:customMessage
PUT /api/v1/devices/:deviceIdentifier/services/:serviceIdentifier/customMessage/:customMessage- Requires
updatepermission onservice.customMessage. customMessageidentifies the message to run.- Request body (if present) and response payload are defined by the device/service implementation.
Typical use cases in UIs:
- “Reboot device”
- “Relearn floors / recalibrate”
- “Trigger test cycle”
Treat custom messages as explicit actions in the UI (buttons, dialogs) rather than regular properties.
7. Open properties
Open properties are used for stream-like binary or string payloads (files, logs, custom streams).
Endpoints:
http
GET /api/v1/services/:serviceIdentifier/devices/:deviceIdentifier/openProperty/:property
GET /api/v1/devices/:deviceIdentifier/services/:serviceIdentifier/openProperty/:property
PUT /api/v1/services/:serviceIdentifier/devices/:deviceIdentifier/openProperty/:property
PUT /api/v1/devices/:deviceIdentifier/services/:serviceIdentifier/openProperty/:propertyRead requires
readonservice.openPropertyWrite requires
updateonservice.openPropertypropertycan be:- Typed property identifier:
0x3000.BIN_STREAM - Or schema property name if a schema is defined for the service
- Typed property identifier:
Request/response payload uses a
datawrapper:jsonc{ "data": "base64-or-string-or-buffer-representation" }
Application patterns:
- Small binary blobs or text logs: read once and display or download
- Multi-part transfer: write in chunks of up to 400 bytes per request
As with other APIs that accept schema names, remember:
- Renaming a property in the schema will break existing URLs that use the name
- For long-lived integrations, prefer typed identifiers; for UI, names are fine
8. Websocket: OBJECT_UPDATED events
The Service module emits OBJECT_UPDATED events when a device updates one or more properties:
type–"OBJECT_UPDATED"deviceIdentifierserviceIdentifier- Optional
identifier(for multi-instance services) timestampupdates– array describing which properties changed, including success/error info
Typical use in an application:
- Open a Websocket using the Websocket module
- Subscribe/route
OBJECT_UPDATEDevents bydeviceIdentifierandserviceIdentifier - For each event:
- Merge changed properties into your frontend state
- Re-run any derived calculations (KPIs, alarms, status labels)
- Update the UI widgets
- If an update corresponds to a property you just wrote:
- Match it (e.g. by property ID) and resolve pending UI actions (spinners, confirmations)
A clean architecture keeps Websocket handling in one place (e.g. a central store) and lets views subscribe to “device/service slices” of the state.
9. Combining Service with other modules
A typical device detail page combines several modules:
- Metadata – find device by search, tenant, or location; show identity info
- Service – read live properties for key services (
status,energy,diagnostics) - Schema – turn raw properties into typed, grouped UI fields
- Timestream – show history and trends for selected properties
- Streams – attach live streams for critical KPIs / charts
- Alarms – show raised alarms that relate to this device and its properties
By keeping Service usage generic and schema-driven, you can reuse the same UI components for different device types and projects.
10. Best practices and pitfalls
Use parsed format by default
Avoidraw=truein regular UI calls. Use it only where you need low-level IDs and modifiers.Let Schema drive the UI
Do not hard-code property lists per service if you can avoid it. Use schemas for names, groups, enums and hints.Be explicit about write permissions
CheckreadonlyandisReadOnlybefore enabling editing. Handle permission errors gracefully.Treat actions differently from parameters
UseisActionableand custom messages to separate “commands” (buttons) from “config parameters” (inputs).Handle missing schema entries
If a property has no schema, you still get it via Service. Show it with a technical label (0x000:TYPE) in advanced views or hide it in simple UIs.Be careful with schema name renames
If you use property names in URLs (openProperty, Streams, alarms, etc.), renaming them in the schema can break those integrations. Prefer typed IDs for long-lived configuration.
With these patterns, the Service module becomes the stable backbone of your device-facing applications.