Skip to content

Historical Data – Timestream Module (History)

This chapter explains how application developers use the Timestream module of the Coldwave Backend to work with historical data and events. It focuses on typical UI and analytics use‑cases and assumes you already understand devices, services and properties from the Service and Schema chapters.


1. What the Timestream module does

The Timestream module provides read‑only access to historical data and events that the Coldwave Backend has stored for your devices. It is the basis for:

  • Dashboards (trend graphs, KPIs, long‑term monitoring)
  • Analytics views (comparison of devices, weekly/daily patterns)
  • Forensics (what happened before an alarm, investigation of incidents)
  • Reporting (export of data for further offline processing)

Internally, the backend organizes history by device, service and property and stores time‑stamped values and events. The Timestream API gives you a flexible way to filter this data and retrieve it in batches.

1.1 History vs. Service vs. Streams

For orientation, use the modules as follows:

ModulePurposeTypical UI usage
ServiceCurrent, live state of devicesDevice detail views, current value tables, controls
StreamsLive streaming of selected propertiesLive charts, oscilloscope‑like views, log consoles
TimestreamHistorical data and events over timeDashboards, trend charts, analytics, reports

The same devices, services and properties are visible across all three modules. Service and Streams give you the current values and live updates, Timestream gives you the history for the same identifiers.


2. High‑level query model

Timestream queries are always iterative:

  1. You start a query with a POST request to /api/v2/timestream/iterator (or /api/v1/timestream/iterator if your backend version does not support v2 yet).
  2. The backend responds with:
    • data: a dictionary with history values (optional)
    • events: a dictionary with history events (optional)
    • done: whether all data has been processed
    • nextToken: a token to request the next batch (if done is false)
    • estimatedQueryProgress: a percentage telling you how far the backend is with scanning the history
  3. While done is false, you continue calling /api/v2/timestream/next/:nextToken to fetch additional batches until all data is delivered.

The query itself is controlled through a JSON body that lets you filter what you want (devices, services, properties, event types) and which time range you are interested in.


3. Typical workflow for a history view

From an application developer’s perspective, a typical “history view” (e.g. a trend chart) follows this flow:

  1. User selects context

    • A device (or a group of devices)
    • One or more services
    • One or more properties (e.g. temperature, doorState)
    • A time range (e.g. last 24 hours, last 7 days, custom range)
  2. Frontend builds a Timestream query

    • Translates the selection into deviceIdentifier, serviceIdentifier and propertyIdentifier lists.
    • Defines from and to timestamps in UTC milliseconds.
    • Chooses whether to fetch data, events, or both.
  3. Start Timestream query

    • POST /api/v2/timestream/iterator
    • Store the returned nextToken and the first batch of data/events in your local application state.
  4. Iteratively fetch remaining batches

    • While done is false, call /api/v2/timestream/next/:nextToken.
    • Append data and events from each batch to your local structures.
    • Use estimatedQueryProgress to update a progress indicator if you like.
  5. Transform the result for visualization

    • Flatten dictionaries into arrays suitable for your charting library.
    • Optionally merge events into the timeline (e.g. vertical markers or event lists below the chart).
    • Compute derived KPIs (min/avg/max, energy consumption per day, etc.).
  6. Render

    • Plot time‑series for each property.
    • Overlay events and highlight relevant situations.
    • Allow the user to change the time range or selection and restart the workflow.

4. Requesting historical data

The main endpoint for starting a history query is:

http
POST /api/v2/timestream/iterator

The request body is a JSON object with the following important fields:

  • queryData (bool, default true): whether to return time‑series data.
  • queryEvents (bool, default true): whether to return persisted events.
  • serviceIdentifier (array of string, optional): services to include.
  • deviceIdentifier (array of string, optional): devices to include.
  • propertyIdentifier (array of string, optional): properties to include.
  • events (array, optional): event types to include.
  • from / to (integers, optional): time range in UTC milliseconds.
  • limit (integer, optional): number of values to return per batch (default 100).
  • prependFirstValue (bool, optional): adds one value before from to get a correct baseline for step functions in graphs.

4.1 Minimal example – last 24 hours of a single property

This example shows how to fetch the last 24 hours of a single property of a single device and service.

jsonc
POST /api/v2/timestream/iterator
Content-Type: application/json

{
  "queryData": true,
  "queryEvents": false,
  "deviceIdentifier": ["DEVICE_123"],
  "serviceIdentifier": ["lift.status"],
  "propertyIdentifier": ["0x0001.UINT16"], // or the internal identifier your backend uses
  "from": 1732200000000,                   // example timestamps, UTC ms
  "to":   1732286400000,
  "limit": 1000,
  "prependFirstValue": true
}

In practice you calculate from/to in your client (for example: to = now, from = now - 24h).

4.2 Filtering by devices, services and properties

  • Devices: Use the deviceIdentifier values from the Service module (/api/v1/devices or /api/v1/services/:serviceIdentifier/devices). These identifiers are consistent across Service, Streams and Timestream.
  • Services: Use serviceIdentifier values from the Service or Schema module (e.g. "lift.status", "hvac.zoneControl").
  • Properties: For Timestream, propertyIdentifier is an array of strings that identify properties. These are usually the typed property identifiers used by the backend (for example in the format 0x0001.UINT16). Use the Schema module to discover which properties a service exposes and how they map to human‑readable names.

TIP

Use the Schema module to drive your UI and keep an internal mapping from human names (for the user) to property identifiers (for Timestream queries). This keeps your queries robust even if you later add translations or adjust display names.

4.3 Data vs. events

  • Set queryData to true if you need time‑series data (for charts, KPIs, etc.).
  • Set queryEvents to true if you also want to know what happened in the same time range (alarms, status changes, system events, etc.).
  • If you only need values for plotting, you can set queryEvents to false to speed up queries.
  • If you only need an event timeline, set queryData to false and queryEvents to true.

5. Iterating over result batches

After starting a query, the backend returns a response like this (simplified):

jsonc
{
  "done": false,
  "nextToken": "abc123",
  "estimatedQueryProgress": 32.5,
  "data": {
    "...": "history data grouped by device and service"
  },
  "events": {
    "...": "events grouped by device"
  }
}

As long as done is false, you should call the next endpoint:

http
POST /api/v2/timestream/next/:nextToken

Example client‑side loop (pseudo‑code):

ts
async function loadHistory(requestBody: TimestreamRequest): Promise<HistoryResult> {
  let combinedData = emptyHistoryStructure();
  let combinedEvents = emptyEventsStructure();

  // 1. Start query
  let res = await fetch("/api/v2/timestream/iterator", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify(requestBody),
  });
  let payload = await res.json();

  mergeHistory(combinedData, payload.data);
  mergeEvents(combinedEvents, payload.events);

  // 2. Fetch further batches while not done
  while (!payload.done && payload.nextToken) {
    const nextRes = await fetch(`/api/v2/timestream/next/${encodeURIComponent(payload.nextToken)}`, {
      method: "POST"
    });
    payload = await nextRes.json();

    mergeHistory(combinedData, payload.data);
    mergeEvents(combinedEvents, payload.events);
  }

  return { data: combinedData, events: combinedEvents };
}

Keep in mind:

  • estimatedQueryProgress can reach 100 before all data has been sent to the client. It means “all important rows have been scanned, but they might still be streaming to you.”
  • /next may temporarily return no data while the backend is still processing a large query. Do not treat an empty batch as an error as long as done is false.

6. Understanding the response structure

The documentation defines data and events as dictionaries. The exact shape can evolve over time, but the important part for application developers is that:

  • data is grouped by device and then by service.
  • events are grouped by device.

A typical conceptual shape looks like this (simplified example, not a literal schema):

jsonc
{
  "data": {
    "DEVICE_123": {
      "lift.status": {
        "0x0001.UINT16": [
          { "timestamp": 1732200000000, "value": 12 },
          { "timestamp": 1732203600000, "value": 13 }
        ],
        "0x0002.BOOL": [
          { "timestamp": 1732200100000, "value": true }
        ]
      }
    },
    "DEVICE_456": {
      "lift.status": {
        "0x0001.UINT16": [ /* ... */ ]
      }
    }
  },
  "events": {
    "DEVICE_123": [
      {
        "timestamp": 1732200500000,
        "type": "ALARM_TRIGGERED",
        "serviceIdentifier": "lift.status",
        "details": { /* event specific payload */ }
      }
    ]
  }
}

When you build your UI, you usually:

  1. Flatten this structure per property for input into your chart library, for example [{ x: timestamp, y: value }, ...].
  2. Group events by type or severity and attach them to the same time axis, e.g. as markers, colored bands or a separate event list.

7. Step charts and prependFirstValue

Many Coldwave properties behave like state variables: a value changes only occasionally and is otherwise constant (e.g. door open/closed, current floor, elevator mode). For these properties, UI often uses step charts.

If you only query data inside your time range, the first visible value might start in the middle of the chart and you do not know what the value was before the range.

The prependFirstValue flag solves this:

  • If true, the backend will add one value right before the from timestamp (if available).
  • This allows you to draw a step chart with the correct initial value at the left border of your graph.
  • The backend has to look further back in time, so the query may take longer.

Use prependFirstValue when:

  • You show stateful properties as step charts.
  • The visual correctness of the initial state is important (e.g. “Was the door already open at 08:00?”).

You can usually keep it false for fast‑changing numeric measurements where the first missing sample is not critical for interpretation.


8. Events in Timestream vs. live events

Timestream can return historical events alongside property values. These events are usually the persisted form of the same event types you see via the Websocket module (for example alarms, status changes, firmware updates, etc.).

Use them in your UI to:

  • Annotate trend charts (e.g. vertical lines for alarm start/clear).
  • Build timelines (“what happened around this time?”).
  • Provide drill‑down for investigations (“show me all events for this device last week”).

Remember that:

  • Timestream events are historical records, independent of whether an alarm is currently active.
  • For current alarm state, use the Alarms module. For “what alarms did this device have in the last 30 days?”, Timestream is the right place.

9. Naming and Schema integration

The Timestream module itself only defines deviceIdentifier, serviceIdentifier and propertyIdentifier, without dictating how you present them to end‑users. The Schema module is what makes them human‑readable.

Recommended pattern:

  1. Use the Schema module to:
    • Discover which services exist and which properties they provide.
    • Get human‑oriented names, units, ranges and groups for properties.
    • Drive your UI (labels, tooltips, grouping, etc.).
  2. Internally, keep a mapping:
    • Human‑readable service nameserviceIdentifier
    • Human‑readable property namepropertyIdentifier
  3. When building Timestream queries:
    • Translate the user’s selection into the corresponding identifiers.
    • Use those identifiers in serviceIdentifier and propertyIdentifier arrays.

For URL‑based endpoints (for example in the Service and Streams modules), many paths accept either:

  • A typed hex property identifier like 0x0001.UINT16, or
  • The property name from the schema.

This is convenient but comes with an important caveat:

WARNING

If you change service or property names in the Schema after building links based on them, URLs that used those names may stop resolving. For long‑lived integrations, prefer identifiers that are stable across schema updates (like the hex property IDs) and keep the mapping to human names internal to your application.


10. Best practices and pitfalls

Prefer the v2 endpoints
Use /api/v2/timestream/iterator and /api/v2/timestream/next/:nextToken whenever possible. They offer better filtering, event support and improved performance compared to v1 and the deprecated GET variants.

Limit your scope

  • Always filter by deviceIdentifier and/or serviceIdentifier where possible.
  • Only include propertyIdentifier values you actually need for the current view.
  • Avoid “global” queries without filters; they can become expensive quickly.

Choose sensible limits

  • Use the limit parameter to cap the number of points per batch.
  • If you need very long time ranges, consider down‑sampling or aggregating data on your side (e.g. daily averages) instead of displaying every single sample.

Handle empty batches

  • While estimatedQueryProgress < 100, /next may return empty data/events even though done is false.
  • Treat this as “still processing”, not as an error.

Keep identifiers stable in your UI

  • Base your UI on Schema names, but keep internal identifiers stable.
  • If you must rename properties in the Schema, plan a migration story for any persistent URLs that used those names directly.

11. Summary

  • Timestream is the history layer of the Coldwave Backend: it stores and returns time‑stamped values and events for your devices.
  • Queries are iterative: you start them with /iterator and fetch all batches via /next.
  • Use Schema + Service to discover devices, services and properties, and then query their history via Timestream.
  • Design your UI around time ranges, device/service/property filters and combined views of data and events.
  • Prefer stable property identifiers in APIs and use Schema names for end‑user presentation.

With these patterns you can build dashboards, analytics views and reports that use the full power of the Coldwave Backend while keeping your integration robust and maintainable.