New: the AI firewall watches every module and explains each incident in plain words. See how it works

Derived values

Compute new telemetry keys in a flow – unit conversions, status codes, consumption from counters, rates, combined values – and store them with Save telemetry.

A derived value is a key your devices do not send but you compute from what they send: a temperature in another unit, a status code, the consumption since the last reading, a rate. You compute it in a flow and store it with Save telemetry, so it is charted, summarised and alarmed on like any other key.

The rule: derive in the device’s own message

Save telemetry stores values only for the device that sent the message (the message’s originator). It refuses any other message with not_a_device. Therefore:

  • Derive values in the root flow that receives the device’s messages, or in a flow the root hands the message to with Run flow (the message, and its device, stay the same).
  • A flow started by a Schedule, Inject, Event or Named timer trigger has the organisation (or the flow) as its originator, not a device. It cannot store device telemetry.
  • A derived value is stored at the record’s time by default, so it lines up with the reading it came from.

Where to put the steps

In the default root flow, telemetry goes from the Message type switch (output telemetry) to Save telemetry. Insert your blocks on that wire:

Device messages → Alarm rules → Message type ─ telemetry → [your blocks] → Save telemetry

Each telemetry message holds one record: msg.payload is a flat object such as {"temperature": 21.5, "humidity": 40}. Add your keys to msg.payload and every key is saved together.

If the alarm rules should also see the derived key, put your blocks before the Alarm rules block, and only on telemetry messages (for example after a Switch on msg.metadata.msgType).

Not every device sends every key in every message. Start your branch with a check (for example a Switch with nempty on msg.payload.power), or the block that computes fails or writes null.

Example: unit conversion

Store temperature_f next to temperature:

  1. Add a Calculate block. expr: round(msg.payload.temperature * 1.8 + 32, 1). target: msg.payload.temperature_f.
  2. Wire it before Save telemetry.

Use a Scale block for a linear range mapping, for example a 4–20 mA signal to 0–10 bar (in_min 4, in_max 20, out_min 0, out_max 10, on the key’s own path).

Example: status code from several values

Store status as a number for charts and site colours: 2 = critical, 1 = warning, 0 = normal.


  1. Add a Calculate block with target msg.payload.status_code and expr:
    msg.payload.temperature > 8 ? 2 : (msg.payload.temperature > 6 ? 1 : 0)

  2. Optionally, a second Calculate with target msg.payload.status and expr msg.payload.status_code == 2 ? "critical" : (msg.payload.status_code == 1 ? "warning" : "ok") for 2D and 3D site views, which read a status key.

Example: consumption from a counter

A meter sends a growing energy_kwh. Store the consumption since the previous reading as energy_delta:

  1. Add a Delta block. Input: msg.payload.energy_kwh. Output: msg.payload.energy_delta. Period: msg.payload.period_ms.
  2. Seed: latest, so the first message after a deploy measures from the stored latest value instead of giving 0.
  3. When negative (on_negative): zero if the meter can restart; then a restart writes 0 and the new value becomes the baseline.
  4. Wire out to Save telemetry. Wire other (messages without the key) straight to Save telemetry too, so other readings are still stored.

The Delta block keeps one previous value per device. For hourly or daily consumption, you do not need a derived key: read the raw counter with the increase aggregate. See Aggregates and rollups.

Example: a rate

From the Delta example, add a Calculate: target msg.payload.power_kw, expr msg.payload.period_ms > 0 ? msg.payload.energy_delta / (msg.payload.period_ms / 3600000) : 0. This is the average power since the previous reading.

Example: combine with a stored value

To compute a value from this message and another key the device sent earlier (for example level now and capacity sent once a day):

  1. Add a Latest values block. keys: capacity. target: msg.metadata.latest. When data is missing: skip.
  2. Add a Calculate: target msg.payload.fill_pct, expr round(msg.payload.level / msg.metadata.latest.capacity * 100, 1).

Values under msg.metadata are never stored; only msg.payload is. For values that belong to the device rather than to a reading (a capacity, a limit), prefer a server attribute and read it with Device details.

Example: smoothing

A Statistics block (window count, size 10, outputs avg) replaces msg.payload with the average of the last 10 values. To keep the original record, use a script, or compute the average on a separate branch and save it there. Derived averages are rarely needed: charts can average with the avg aggregate.

Things to keep in mind

TopicDetail
Key capEach derived key is a new key and counts toward your plan’s key cap. See Telemetry keys.
Script blocksA Script block can compute anything, but counts toward your plan’s script blocks. Prefer Calculate, Change and Delta.
MemoryDelta, Statistics and similar blocks remember values per device. Deploying a change to such a block’s settings starts its memory empty.
Late dataLate messages run through the root flow and are stored, derived values included.
TestingTest a flow with a sample message before you deploy: nothing is stored in a test. See Versions, testing and rollback.
Other recordsOn the General host, the Save reading block stores readings of other record kinds that accept writes from flows (for example asset meters, where your plan includes them). Device data always goes through Save telemetry.

See also IoT data blocks, Data transform blocks and the Flow cookbook.