Skip to content

Legacy Sensor MQTT Specification

This document specifies the MQTT message format for legacy (non-BLE) MQTT-enabled sensors. Devices that implement this spec are discovered by the server and registered as sensor assets, feeding the same storage and alerting pipeline as BLE sensor assets.

The format is intentionally minimal: it carries only fields the system consumes. Parsing is handled by MqttSensorDataSchema in lib/src/schemas.ts.

Transport

ItemValue
ProtocolMQTT 3.1.1 or later
Topicsafecall/sensor/{DMAC} (e.g. safecall/sensor/A1B2C3D4E5F6)
QoS0
Retainfalse
CredentialsPer-deployment broker username/password (same broker as server_mqtt_url)

The server subscribes to # and routes on the payload msg field, not the topic. The topic above is still normative for devices — it keeps the broker organized and allows per-device ACLs (restrict each device to publishing on its own topic).

Payload

A single flat JSON object, UTF-8 encoded, maximum 256 bytes. No envelope, no batching — one reading per message.

FieldTypeRequiredDescription
msgstringyesMessage discriminator. Must be exactly "sData".
vintegeryesSpec version. Must be 1.
dmacstringyesDevice MAC address: 12 uppercase hex characters, no separators (^[0-9A-F]{12}$).
tempnumberyesBox or primary temperature in °C. At most 2 decimal places recommended.
humnumber or "nan"noRelative humidity in % (0–100). Send "nan" or omit the field when no humidity value is available.
temp2numbernoEvaporator or secondary temperature in °C.
defrostnumbernoDefrost status, normally 0 or 1. The server stores the raw numeric value.
doorswitchnumbernoDoor switch status, normally 0 or 1. The server stores the raw numeric value.
fridge_idnumbernoVendor fridge identifier (typically 1–8). Used as import metadata and, when in 1..8, to derive a virtual asset MAC (see below).
firdge_idnumbernoVendor typo accepted for compatibility; normalized internally to fridge_id.

Messages with missing required fields, a malformed dmac, or an unknown msg/v value are silently dropped. Unknown extra fields are ignored by the server; devices should not send them.

Shared ethernet MAC / virtual asset identity

Some fridge controllers publish multiple sData messages with the same physical dmac (one ethernet NIC) and different fridge_id values. The server keeps beacons.mac UNIQUE by rewriting identity at ingest:

  • When fridge_id is an integer in 1..8, the asset MAC becomes
    FE + first 9 hex digits of physical dmac + fridge_id as one hex digit.
    Example: physical 70B8F66C3C33, fridge 6FE70B8F66C36.
  • Devices keep publishing the physical dmac on the wire; only SafeCall remaps server-side (discovery, import, sensor_data, alerts).
  • The original ethernet address is stored on the asset as beacons.physical_mac (shown read-only on the Assets edit form).
  • Import Assets sets type to sensor when discovery has fridge_id.
  • Debug Device matches subscriptions against either the physical wire dmac or the derived virtual MAC.
  • fridge_id outside 1..8 (or absent) leaves dmac unchanged.
  • BLE adv1 / adv4 / adv8 paths are never rewritten.

If a shared physical MAC was already imported before this mapping, delete or re-import so assets use the virtual MACs. Existing virtual-MAC assets missing physical_mac are backfilled on the next sData reading.

Example

json
{"msg":"sData","v":1,"dmac":"A1B2C3D4E5F6","temp":22.5,"hum":45.2}

About 65 bytes on the wire.

Fridge example

json
{"temp":18.8,"temp2":18.8,"defrost":0,"doorswitch":0,"msg":"sData","v":1,"dmac":"70B8F66C3C33","firdge_id":6,"hum":"nan"}

This message is discovered and stored under virtual MAC FE70B8F66C36 (import default name still Fridge 6).

Publish Cadence

Publish one message every 60 seconds (recommended default). The server throttles persistence per beacon via its update_interval setting, so publishing more frequently only improves alert latency, not stored resolution.

Server Processing

Device ──sData──▶ MQTT Broker ──▶ lib/src/mqtt.ts (parse + validate)


                          server/src/mqtt.ts (dispatch "sdata")
                              ├── devices_mqtt_process()
                              │   → virtual MAC if fridge_id 1–8
                              │   → discovery cache → device_beacons
                              └── events_process_mqtt_message()
                                  → virtual MAC if fridge_id 1–8
                                  → sensor_data storage (temperature, optional humidity/fridge metrics)
                                  → range alerts (ntfy "sensors" topic)
  • Discovery — every valid message updates the in-memory device cache immediately; device_beacons is updated periodically from that cache. Admin Import Beacons reads both sources, so a new device can be imported as soon as MQTT is received.
  • Storage — temperature and any available humidity/fridge metrics are written to the sensor database, at most once per update_interval seconds.
  • Alerts — readings are checked against the beacon's temperature_range / humidity_range (after applying the optional coefficients) on every message; out-of-range values trigger a notification.
  • No positioning — these devices publish directly (no gateway, no RSSI), so they never participate in RTLS tracking and have no gateway sightings.

Registration

Sensor data is only stored and alerted on for active, registered assets:

  1. Power the device and let it publish at least one valid message.
  2. In the admin UI, open Assets and use Import Assets — the device is listed by its MAC from discovery (live MQTT cache plus device_beacons).
  3. Edit the imported asset: set type to sensor, mark it active, and configure update_interval, temperature_range / humidity_range, and coefficients as needed.

Until registered, messages still update discovery (device_beacons) but nothing is stored.

Versioning

  • Any backwards-incompatible change to this format bumps v; the server validates v strictly per version.
  • Field meanings and units must never change within a version.
  • New optional fields can be added within version 1 when old devices remain valid and field meanings do not change.

See Also