Skip to content

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:

ModuleWhat it gives you
ServiceCurrent property values and commands
SchemaHuman-readable names, enums, units, UI hints
MetadataDevice identity, tags, location, ownership
TimestreamHistorical values and trends
StreamsLive updates over Websocket for selected properties
AlarmsRules 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 numeric propId and includes modifiers
  • Parsed format (raw=false, default): dictionary keyed by schema names (or 0x000:TYPE if no schema)

2.2 Property modifiers and tags

In raw mode, each property has:

  • propId – numeric identifier
  • type – backend type (INT32, FLOAT, BOOL, STRING, …)
  • value – current value (if present)
  • modifier flags, for example:
    • isReadOnly – cannot be written
    • isActionable – “command-like” property requiring acknowledgement
    • isVolatile – value might not be persisted
    • isError – last operation failed
    • isMeta – 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 read permission on service.
  • 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.status service”

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/devices
  • serviceIdentifier – 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/services
  • deviceIdentifier – 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 read permission on service.
  • Same query parameters: raw, hex, depth.

Recommended usage for UI code:

  • Use raw=false and hex=false to get parsed, schema-based dictionaries where keys are property names.
  • Use raw=true when you need low-level propId, 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:

  1. Call GET /api/v1/devices/:device/services/:service?raw=false
  2. Look up ServiceSchema for serviceIdentifier
  3. For each schema property:
    • Find the corresponding value in the Service response (by schema name)
    • Create a UiProperty that contains id, label, type, value, readonly, enum options, group, etc.
  4. 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 update permission on service.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

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 isError flags (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 update permission on service.customMessage.
  • customMessage identifies 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/:property
  • Read requires read on service.openProperty

  • Write requires update on service.openProperty

  • property can be:

    • Typed property identifier: 0x3000.BIN_STREAM
    • Or schema property name if a schema is defined for the service
  • Request/response payload uses a data wrapper:

    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"
  • deviceIdentifier
  • serviceIdentifier
  • Optional identifier (for multi-instance services)
  • timestamp
  • updates – array describing which properties changed, including success/error info

Typical use in an application:

  1. Open a Websocket using the Websocket module
  2. Subscribe/route OBJECT_UPDATED events by deviceIdentifier and serviceIdentifier
  3. For each event:
    • Merge changed properties into your frontend state
    • Re-run any derived calculations (KPIs, alarms, status labels)
    • Update the UI widgets
  4. 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:

  1. Metadata – find device by search, tenant, or location; show identity info
  2. Service – read live properties for key services (status, energy, diagnostics)
  3. Schema – turn raw properties into typed, grouped UI fields
  4. Timestream – show history and trends for selected properties
  5. Streams – attach live streams for critical KPIs / charts
  6. 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
    Avoid raw=true in 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
    Check readonly and isReadOnly before enabling editing. Handle permission errors gracefully.

  • Treat actions differently from parameters
    Use isActionable and 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.