Thingsee CBOR to JSON Conversion
Overview
Thingsee sensors transmit data using CBOR (Concise Binary Object Representation), a compact binary format optimized for low-power wireless communication. Applications and cloud platforms consume this data as JSON, requiring conversion at the gateway or backend level.
This guide provides:
- Complete CBOR-to-JSON mapping tables
- Conversion examples with real sensor messages
- Decoder implementations in Python and JavaScript
- Wirepas endpoint configuration details
Why CBOR?
CBOR reduces message size by ~60-70% compared to JSON, which:
- Extends battery life (fewer radio transmissions)
- Reduces network bandwidth consumption
- Enables more frequent sensor updates
- Supports larger mesh networks
Message Structure
CBOR Format (Sensor → Gateway)
CBOR messages contain only essential sensor data:
Header fields:
- tsmId (CBOR index 1): Message ID
- tsmEv (CBOR index 2): Event ID
Data fields:
- Device-specific measurements (CBOR indexes 20-1211)
Gateway or backend must add:
tsmTs- Timestamp (from Wirepas time-in-transit)tsmTuid- Device serial (from Wirepas node ID)tsmGw- Gateway identifier (optional)
JSON Format (Application Layer)
Complete JSON structure with all fields populated:
[{
"tsmId": 13100,
"tsmEv": 10,
"tsmTs": 1492603998,
"tsmTuid": "TSPR04123456",
"tsmGw": "TSGW2G987654",
"tsmAp": "occupancy-monitoring",
"moveCount": 7
}]
CBOR Mapping Tables
Header Fields
| JSON Property | CBOR Index | Origin | Example | Description |
|---|---|---|---|---|
| tsmId | 1 | Sensor | 13100 | Message ID identifying data structure |
| tsmEv | 2 | Sensor | 10 | Event ID indicating trigger reason |
| tsmTs | 3 | Gateway | 1492844596 | UTC timestamp in seconds |
| tsmTuid | 4 | Gateway | “TSPR04123” | Device serial number |
| tsmGw | 5 | Gateway | “TSGW2G321” | Gateway identifier |
| tsmAp | 6 | Cloud | “demo-app” | Application context |
| tsmDstTuid | 7 | Cloud | “TSGW2G321” | Destination device (for commands) |
| tsmGwTs | 8 | Cloud | 1492844601 | Gateway receipt timestamp |
| tsmCloudTs | 9 | Cloud | 1492844602 | Cloud receipt timestamp |
Data Fields (Common)
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| pwr | 20 | 0 | 0 | Power status (0=OFF, 1=ON) |
| batl | 21 | 1 | 882 | Battery level (88.2% in example) |
| chrg | 22 | 0 | 2 | Charger status (0=Not connected, 1=Charging, 2=Connected not charging) |
| requestTsmId | 23 | 0 | 1310 | Requested message ID from cloud |
| timestamp | 24 | 0 | 1493312188 | Epoch timestamp |
| transactionId | 25 | 0 | 1493312188 | Transaction ID for request-response sync |
| uptime | 26 | 0 | 3600 | Seconds since device startup |
| status | 27 | 0 | 0 | Status code (message-specific) |
Sensor Measurements
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| temp | 30 | 1 | 224 | Temperature in Celsius (22.4°C in example) |
| lght | 31 | 0 | 720 | Light in lux |
| humd | 32 | 1 | 724 | Humidity 0-100% (72.4% in example) |
| airp | 33 | 3 | 102092358 | Air pressure in Pascals (102092.358 Pa in example) |
| level | 34 | 0 | 0 | Level (message-specific unit) |
| min | 35 | 0 | 0 | Minimum value (message-specific) |
| max | 36 | 0 | 0 | Maximum value (message-specific) |
| mode | 37 | 0 | 0 | Operating mode (message-specific) |
| state | 38 | 0 | 0 | Device state (message-specific) |
| pressure | 39 | 3 | 102092358 | Pressure in Pascals |
Motion and Position
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| accx | 40 | 0 | 0 | X-axis acceleration |
| accy | 41 | 0 | -100 | Y-axis acceleration |
| accz | 42 | 0 | 900 | Z-axis acceleration |
| move | 43 | 0 | 1 | Movement detected (0=No, 1=Yes) |
| moveCount | 44 | 0 | 7 | Movement count since last report |
| dist | 45 | 0 | 152 | Distance in millimeters |
| hall | 46 | 0 | 1 | Hall sensor state (0=Open, 1=Closed) |
| hallCount | 47 | 0 | 1 | Hall sensor state change count |
| switch | 48 | 0 | 1 | Switch state (0=Open, 1=Closed) |
| switchCount | 49 | 0 | 1 | Switch state change count |
| gforce | 56 | 0 | 1012 | G-force sum vector in milli-g |
| gforceMax | 57 | 0 | 3567 | Maximum g-force in milli-g |
| angle | 190 | 1 | 900 | Angular position in degrees (90.0° in example) |
| duration | 191 | 0 | 1800 | Duration in seconds |
Network and Device Info
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| voltage | 59 | 6 | 3000000 | Voltage in volts (3.0V in example) |
| wpnode | 60 | N/A | ‘123456’ | Wirepas node ID (string) |
| rssi | 61 | 0 | 78 | RSSI value (0-255 scale) |
| tuid | 62 | N/A | ‘S3R14LNUMB3R’ | Cloud identifier (string) |
| rssiDbm | 65 | 0 | -70 | RSSI in dBm |
| swVersion | 70 | N/A | ‘sensorFW1.0.2’ | Software version (string) |
| modelCode | 71 | N/A | ‘TSGW2G’ | Product model code (string) |
Activity and Energy
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| activityLevel | 81 | 3 | 98000 | Activity level indicator 0-100 (98.000 in example) |
| energyLevel | 89 | 0 | 2647 | Cumulative energy volume as force vector sum |
| energyLevelThreshold | 291 | 0 | 1 | Energy level threshold |
| activityLevelThreshold | 292 | 3 | 1 | Activity level threshold |
Air Quality
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| carbonDioxide | 283 | 0 | 624 | CO2 in ppm |
| tvoc | 284 | 0 | 50 | Total VOC |
| carbonDioxideHysteresis | 281 | 0 | 10 | CO2 hysteresis threshold |
| tvocHysteresis | 282 | 0 | 5 | TVOC hysteresis threshold |
Measurement Data
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| resistance | 111 | 0 | 1000 | Resistance in ohms |
| index | 112 | 0 | 1 | Index value |
| count | 113 | 0 | 1 | Count value |
| messageIndex | 146 | 0 | 1 | Message sequence index |
| deviceState | 147 | 0 | 1 | Device state indicator |
| restartCount | 154 | 0 | 1 | Device restart count |
| lastUpdate | 170 | 0 | 0 | Time since last update |
| static | 171 | 0 | 0 | Static indicator |
| length | 181 | 0 | 0 | Length value |
| data | 182 | N/A | - | Generic data field |
| command | 183 | N/A | - | Command field |
| payload | 184 | N/A | - | Command payload |
Configuration Parameters
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| measurementInterval | 256 | 0 | 60 | Measurement interval in seconds |
| measurementFrequency | 257 | 0 | 25 | Measurement frequency in Hz |
| reportInterval | 258 | 0 | 300 | Reporting interval in seconds |
| impactThresholdMin | 259 | 0 | 200 | Lower impact threshold in mG |
| impactThresholdMax | 260 | 1 | 1000 | Upper impact threshold in mG |
| idleInterval | 261 | 0 | 3600 | Idle interval in seconds |
| accelerometerMode | 262 | 0 | 0 | Accelerometer configuration mode |
| threshold | 263 | 0 | 0 | Generic threshold value |
| enableLed | 264 | 0 | 0 | LED control setting |
| weatherMode | 266 | 0 | 0 | Weather sensor configuration |
| hallMode | 267 | 0 | 0 | Hall sensor configuration |
| leakageMode | 268 | 0 | 0 | Leakage sensor configuration |
| autoCalibration | 269 | N/A | - | Auto-calibration setting |
| calibrationValue | 270 | N/A | - | Calibration value |
| filterValue | 271 | N/A | - | Filter value |
| commandId | 272 | 0 | 1493312188 | Command transaction ID |
| temperatureMode | 273 | 0 | 0 | Temperature sensor mode |
| intervalMultiplier | 274 | 0 | 0 | Interval multiplier |
| temperatureHysteresis | 275 | 1 | 5 | Temperature hysteresis (0.5°C in example) |
| humidityHysteresis | 276 | 1 | 20 | Humidity hysteresis (2.0% in example) |
| pressureHysteresis | 277 | 3 | 20000 | Pressure hysteresis (20 Pa in example) |
| lightHysteresis | 278 | 0 | 100 | Light hysteresis in lux |
| resistanceHysteresis | 279 | 0 | 10000 | Resistance hysteresis in ohms |
| orientationHysteresis | 280 | 0 | 5 | Orientation hysteresis in degrees |
| staticFilter | 285 | 0 | 2 | Static filter setting |
| dynamicFilter | 286 | 0 | 1 | Dynamic filter setting |
| samples | 287 | 0 | 8 | Sample count |
| averaging | 288 | 0 | 1 | Averaging setting |
| rounding | 289 | 0 | 1 | Rounding setting |
| pressureCompensate | 290 | 0 | 1 | Pressure compensation setting |
| autoRole | 543 | 0 | 0 | Wirepas auto-role setting |
| lowLatency | 544 | 0 | 0 | Wirepas low-latency setting |
Profile and Statistics
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| profileId | 1100 | 0 | 0 | Configuration profile ID |
| tempMin | 1101 | 1 | -23 | Minimum temperature (-2.3°C in example) |
| tempMax | 1102 | 1 | 156 | Maximum temperature (15.6°C in example) |
| humdMin | 1103 | 1 | 556 | Minimum humidity (55.6% in example) |
| humdMax | 1104 | 1 | 612 | Maximum humidity (61.2% in example) |
| fromTimestamp | 1105 | 0 | 1493312188 | Period start timestamp |
Occupancy Histogram
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| histogram0 | 1000 | 0 | 2 | Detection count in area index 0 |
| histogram1 | 1001 | 0 | 7 | Detection count in area index 1 |
| histogram2 | 1002 | 0 | 13 | Detection count in area index 2 |
| histogram3 | 1003 | 0 | 45 | Detection count in area index 3 |
| histogram4 | 1004 | 0 | 234 | Detection count in area index 4 |
| histogram5 | 1005 | 0 | 153 | Detection count in area index 5 |
| histogram6 | 1006 | 0 | 78 | Detection count in area index 6 |
| histogram7 | 1007 | 0 | 45 | Detection count in area index 7 |
| histogram8 | 1008 | 0 | 32 | Detection count in area index 8 |
| histogram9 | 1009 | 0 | 2 | Detection count in area index 9 |
People Counting
| JSON Property | CBOR Index | Multiplier (10^x) | Example | Description |
|---|---|---|---|---|
| in | 1210 | 0 | 10 | Ingress count (people entering) |
| out | 1211 | 0 | 8 | Egress count (people leaving) |
Event IDs (tsmEv)
Event IDs indicate why the message was sent:
| Event ID | Name | Description |
|---|---|---|
| 0 | No event | Event data not applicable or unknown |
| 7 | State change event | Result of a state change |
| 8 | Key event | Reactive events (sensor-triggered, key press) |
| 9 | Threshold event | Threshold exceeded |
| 10 | Timed event | Streaming event sent at defined intervals |
| 11 | Thing powered ON | Device boot-up |
| 12 | Thing powered OFF | Device powering off |
| 13 | Thing controlled reset occurred | Controlled reset performed |
| 14 | Thing uncontrolled reset occurred | Uncontrolled reset occurred |
| 15 | Thing firmware updated | Firmware update completed |
| 16 | Thing software configuration updated | Software configuration changed |
| 17 | Network connected | Connected to gateway/network |
| 18 | Network disconnected | Disconnected from gateway/network |
| 19 | Assistance data requested | Request for assistance data from cloud |
| 20 | Assistance data response | Response to assistance data request |
| 21 | Power ON requested | Cloud request to power ON |
| 22 | Power OFF requested | Cloud request to power OFF |
| 23 | Reset requested | Cloud request to reset |
| 25 | Firmware update requested | Cloud request for firmware update |
| 26 | Software configuration update requested | Cloud request for configuration update |
| 27 | Diagnostics requested | Cloud request to enable diagnostics |
| 28 | Requested diagnostic event | Remotely enabled diagnostic event |
| 29 | Error diagnostic event | Critical error report |
| 30 | Application profile update requested | Device must update behavior |
| 31 | Application profile update response | Response to profile update request |
| 32 | Firmware information request | Request for firmware information |
| 33 | Firmware information response | Response to firmware information request |
| 34 | Timed diagnostics event | Periodic diagnostic event |
| 35 | Command request | Command sent to device |
| 36 | Command response | Response to command |
Conversion Examples
Example 1: Thingsee PRESENCE Occupancy
Raw hex message:
9f a3 01 19 33 2c 02 0a 18 2c 07 ff
Step 1: CBOR decode
[{1: 13100, 2: 10, 44: 7}]
Step 2: Apply CBOR index mapping
[{
"tsmId": 13100,
"tsmEv": 10,
"moveCount": 7
}]
Step 3: Add gateway-originated fields
[{
"tsmId": 13100,
"tsmEv": 10,
"tsmTs": 1492844596,
"tsmTuid": "TSPR04123456",
"tsmGw": "TSGW2G987654",
"moveCount": 7
}]
Interpretation:
- Device: Thingsee PRESENCE (message ID 13100)
- Event: Timed event (event ID 10)
- Data: 7 movements detected since last report
Example 2: Thingsee RADAR Occupancy
Raw hex message:
9f a5 01 19 6d 69 02 07 19 04 ff 19 06 21 18 26 01 18 2d 19 06 40 ff
Step 1: CBOR decode
[{1: 28009, 2: 7, 1279: 1569, 38: 1, 45: 1600}]
Step 2: Apply CBOR index mapping
[{
"tsmId": 28009,
"tsmEv": 7,
"echo": 1569,
"state": 1,
"dist": 1600
}]
Note: Index 1279 (“echo”) is device-specific and not in the common mapping table. Refer to device-specific documentation for custom indexes.
Step 3: Add gateway fields and apply multipliers
[{
"tsmId": 28009,
"tsmEv": 7,
"tsmTs": 1492844596,
"tsmTuid": "TSRAD5123456",
"dist": 1600,
"echo": 1569,
"state": 1
}]
Interpretation:
- Device: Thingsee RADAR (message ID 28009)
- Event: State change event (event ID 7)
- Data: Object detected at 1600mm distance, occupied state
Example 3: Configuration Message (Downlink)
JSON configuration request:
{
"tsmId": 1500,
"tsmEv": 30,
"transactionId": 1485925806,
"measurementInterval": 30,
"reportInterval": 300
}
Step 1: Convert to CBOR indexes
{1: 1500, 2: 30, 25: 1485925806, 256: 30, 258: 300}
Step 2: CBOR encode to binary
a5 01 19 05 dc 02 18 1e 18 19 1a 58 91 6d ae 19 01 00 18 1e 19 01 02 19 01 2c
Important: Wirepas endpoints must be 21/21 for sensor configuration messages.
Wirepas Endpoint Configuration
Thingsee sensors use different Wirepas endpoints depending on the CBOR format:
Endpoint 21/21 (Thingsee CBOR)
Use case: Standard Thingsee sensor messages
Format: CBOR with Thingsee message specification
MQTT topic structure:
haltian-iot/wirepas/gw-event/received_data/{gateway-id}/sink1/{network-id}/21/21
Characteristics:
- Uses complete mapping table (indexes 1-1211)
- Compatible with all Thingsee sensors
- Standard for configuration (downlink) messages
- Most common format in Haltian IoT deployments
Endpoint 247/255 (Wirepas CBOR)
Use case: Wirepas reference implementation format
Format: CBOR following Wirepas standard
MQTT topic structure:
haltian-iot/wirepas/gw-event/received_data/{gateway-id}/sink1/{network-id}/247/255
Characteristics:
- May use different CBOR map version (5.1 vs 5.2)
- Interoperable with Wirepas ecosystem devices
- Different header structure compared to endpoint 21/21
Endpoint 238/238 (TLV Format)
Use case: Tag-Length-Value encoding (legacy)
Format: TLV binary format
MQTT topic structure:
haltian-iot/wirepas/gw-event/received_data/{gateway-id}/sink1/{network-id}/238/238
Characteristics:
- Not CBOR format
- Used by specific legacy devices
- Requires separate decoder implementation
Configuration Requirements
For sensor configuration (downlink):
- Endpoints must be 21/21 - sensors reject messages from other endpoints
- Include
transactionId(CBOR index 25) for request-response correlation - Sensor replies with same
tsmId, all configuration fields populated with current values
For monitoring network traffic:
- Subscribe to all endpoints:
#wildcard - Filter by source endpoint to identify message format
- Use appropriate decoder for each endpoint type
Decoder Implementations
Python Decoder
import cbor2
import struct
from datetime import datetime
# CBOR index to JSON property mapping
CBOR_INDEX_MAP = {
# Header
1: "tsmId",
2: "tsmEv",
3: "tsmTs",
4: "tsmTuid",
5: "tsmGw",
6: "tsmAp",
7: "tsmDstTuid",
8: "tsmGwTs",
9: "tsmCloudTs",
# Common data fields
20: "pwr",
21: "batl",
22: "chrg",
23: "requestTsmId",
24: "timestamp",
25: "transactionId",
26: "uptime",
27: "status",
# Sensor measurements
30: "temp",
31: "lght",
32: "humd",
33: "airp",
34: "level",
35: "min",
36: "max",
37: "mode",
38: "state",
39: "pressure",
# Motion
40: "accx",
41: "accy",
42: "accz",
43: "move",
44: "moveCount",
45: "dist",
46: "hall",
47: "hallCount",
48: "switch",
49: "switchCount",
56: "gforce",
57: "gforceMax",
# Network
59: "voltage",
60: "wpnode",
61: "rssi",
62: "tuid",
65: "rssiDbm",
70: "swVersion",
71: "modelCode",
# Activity
81: "activityLevel",
89: "energyLevel",
# Configuration
256: "measurementInterval",
257: "measurementFrequency",
258: "reportInterval",
259: "impactThresholdMin",
260: "impactThresholdMax",
261: "idleInterval",
262: "accelerometerMode",
263: "threshold",
264: "enableLed",
# Air quality
283: "carbonDioxide",
284: "tvoc",
# Histogram
1000: "histogram0",
1001: "histogram1",
1002: "histogram2",
1003: "histogram3",
1004: "histogram4",
1005: "histogram5",
1006: "histogram6",
1007: "histogram7",
1008: "histogram8",
1009: "histogram9",
# People counting
1210: "in",
1211: "out",
}
# Multipliers for fields requiring decimal conversion
MULTIPLIERS = {
"batl": 1,
"temp": 1,
"humd": 1,
"airp": 3,
"pressure": 3,
"voltage": 6,
"activityLevel": 3,
"impactThresholdMax": 1,
"angle": 1,
"tempMin": 1,
"tempMax": 1,
"humdMin": 1,
"humdMax": 1,
"temperatureHysteresis": 1,
"humidityHysteresis": 1,
"pressureHysteresis": 3,
"activityLevelThreshold": 3,
}
def decode_cbor_message(binary_payload):
"""
Decode CBOR binary payload to JSON structure.
Args:
binary_payload: bytes - Raw CBOR binary data
Returns:
dict - Decoded message with JSON property names
"""
try:
# Decode CBOR to Python dict
cbor_data = cbor2.loads(binary_payload)
# Handle array wrapper (messages typically come as single-element arrays)
if isinstance(cbor_data, list):
cbor_data = cbor_data[0] if len(cbor_data) > 0 else {}
# Convert CBOR indexes to JSON property names
json_message = {}
for cbor_index, value in cbor_data.items():
# Get JSON property name
prop_name = CBOR_INDEX_MAP.get(cbor_index, f"unknown_{cbor_index}")
# Apply multiplier if needed
if prop_name in MULTIPLIERS:
multiplier = MULTIPLIERS[prop_name]
value = value / (10 ** multiplier)
json_message[prop_name] = value
return json_message
except Exception as e:
print(f"Error decoding CBOR: {e}")
return None
def add_gateway_fields(json_message, wirepas_node_id, gateway_id, network_timestamp):
"""
Add gateway-originated fields to sensor message.
Args:
json_message: dict - Decoded sensor message
wirepas_node_id: int - Wirepas node ID
gateway_id: str - Gateway identifier
network_timestamp: int - Wirepas network timestamp
Returns:
dict - Complete message with gateway fields
"""
# Add timestamp (convert Wirepas time to UTC)
json_message["tsmTs"] = network_timestamp
# Add device identifier (lookup serial from node ID mapping)
# In production, query device database with wirepas_node_id
json_message["tsmTuid"] = f"NODE{wirepas_node_id}"
# Add gateway identifier
json_message["tsmGw"] = gateway_id
return json_message
# Example usage
if __name__ == "__main__":
# Example: Thingsee PRESENCE movement message
hex_message = "9fa3011933 2c020a182c07ff"
binary_payload = bytes.fromhex(hex_message.replace(" ", ""))
# Decode
message = decode_cbor_message(binary_payload)
print("Decoded message:", message)
# Output: {'tsmId': 13100, 'tsmEv': 10, 'moveCount': 7}
# Add gateway fields
complete_message = add_gateway_fields(
message,
wirepas_node_id=123456,
gateway_id="TSGW2G987654",
network_timestamp=1492844596
)
print("Complete message:", complete_message)
JavaScript Decoder
const cbor = require('cbor');
// CBOR index to JSON property mapping
const CBOR_INDEX_MAP = {
// Header
1: 'tsmId',
2: 'tsmEv',
3: 'tsmTs',
4: 'tsmTuid',
5: 'tsmGw',
6: 'tsmAp',
7: 'tsmDstTuid',
8: 'tsmGwTs',
9: 'tsmCloudTs',
// Common data fields
20: 'pwr',
21: 'batl',
22: 'chrg',
23: 'requestTsmId',
24: 'timestamp',
25: 'transactionId',
26: 'uptime',
27: 'status',
// Sensor measurements
30: 'temp',
31: 'lght',
32: 'humd',
33: 'airp',
38: 'state',
44: 'moveCount',
45: 'dist',
// Add more mappings as needed...
// People counting
1210: 'in',
1211: 'out'
};
// Multipliers for decimal conversion
const MULTIPLIERS = {
batl: 1,
temp: 1,
humd: 1,
airp: 3,
voltage: 6,
activityLevel: 3,
angle: 1
};
/**
* Decode CBOR binary payload to JSON structure
* @param {Buffer} binaryPayload - Raw CBOR binary data
* @returns {Object} Decoded message with JSON property names
*/
function decodeCborMessage(binaryPayload) {
try {
// Decode CBOR to JavaScript object
let cborData = cbor.decodeFirstSync(binaryPayload);
// Handle array wrapper
if (Array.isArray(cborData)) {
cborData = cborData.length > 0 ? cborData[0] : {};
}
// Convert CBOR indexes to JSON property names
const jsonMessage = {};
for (const [cborIndex, value] of Object.entries(cborData)) {
const index = parseInt(cborIndex);
// Get JSON property name
const propName = CBOR_INDEX_MAP[index] || `unknown_${index}`;
// Apply multiplier if needed
let finalValue = value;
if (propName in MULTIPLIERS) {
const multiplier = MULTIPLIERS[propName];
finalValue = value / Math.pow(10, multiplier);
}
jsonMessage[propName] = finalValue;
}
return jsonMessage;
} catch (error) {
console.error('Error decoding CBOR:', error);
return null;
}
}
/**
* Add gateway-originated fields to sensor message
* @param {Object} jsonMessage - Decoded sensor message
* @param {number} wirepasNodeId - Wirepas node ID
* @param {string} gatewayId - Gateway identifier
* @param {number} networkTimestamp - Wirepas network timestamp
* @returns {Object} Complete message with gateway fields
*/
function addGatewayFields(jsonMessage, wirepasNodeId, gatewayId, networkTimestamp) {
// Add timestamp
jsonMessage.tsmTs = networkTimestamp;
// Add device identifier (lookup from database in production)
jsonMessage.tsmTuid = `NODE${wirepasNodeId}`;
// Add gateway identifier
jsonMessage.tsmGw = gatewayId;
return jsonMessage;
}
// Example usage
const hexMessage = '9fa3011933 2c020a182c07ff';
const binaryPayload = Buffer.from(hexMessage.replace(/\s/g, ''), 'hex');
// Decode
const message = decodeCborMessage(binaryPayload);
console.log('Decoded message:', message);
// Output: { tsmId: 13100, tsmEv: 10, moveCount: 7 }
// Add gateway fields
const completeMessage = addGatewayFields(
message,
123456,
'TSGW2G987654',
1492844596
);
console.log('Complete message:', completeMessage);
Handling Compressed Messages
Some devices send compressed CBOR using zlib. Add decompression before CBOR decoding:
Python with zlib:
import zlib
import cbor2
def decode_compressed_cbor(binary_payload):
try:
# Decompress with zlib
decompressed = zlib.decompress(binary_payload)
# Decode CBOR
cbor_data = cbor2.loads(decompressed)
# Process as normal...
return cbor_data
except zlib.error:
# Not compressed, decode directly
return cbor2.loads(binary_payload)
JavaScript with pako:
const pako = require('pako');
const cbor = require('cbor');
function decodeCompressedCbor(binaryPayload) {
try {
// Try decompression
const decompressed = pako.inflate(binaryPayload);
const cborData = cbor.decodeFirstSync(Buffer.from(decompressed));
return cborData;
} catch (error) {
// Not compressed, decode directly
return cbor.decodeFirstSync(binaryPayload);
}
}
Best Practices
Decoder Implementation
Use established CBOR libraries - Don’t implement CBOR parser from scratch
- Python:
cbor2 - JavaScript:
cbor - Java:
com.fasterxml.jackson.dataformat:jackson-dataformat-cbor - C#:
PeterO.Cbor
- Python:
Handle unknown indexes gracefully - New firmware may introduce fields not in your mapping table
prop_name = CBOR_INDEX_MAP.get(cbor_index, f"unknown_{cbor_index}")Validate message structure - Check for required fields before processing
if "tsmId" not in message or "tsmEv" not in message: logger.warning("Invalid message structure") return NoneLog unknown indexes - Track unmapped indexes to update decoder
if cbor_index not in CBOR_INDEX_MAP: logger.info(f"Unknown CBOR index {cbor_index} with value {value}")
Timestamp Handling
- Sensor messages don’t include timestamps - Gateway must add from Wirepas time-in-transit
- Use UTC timestamps - All
tsmTsvalues are UTC epoch seconds - Precision - Can use decimals for millisecond resolution (e.g.,
1492844596.123) - Network disconnected events (tsmEv: 18) - May lack
tsmTs, cloud should generate
Device Identification
- Wirepas node ID - Primary identifier in raw CBOR messages
- Serial number lookup - Map node ID to serial via device database/API
- Use Service API - Query for complete device details including serial, name, model
query DeviceByWirepasNode($nodeId: Int!) {
devices(where: {identifiers: {wirepasNodeId: {_eq: $nodeId}}}) {
id
name
identifiers {
wirepasNodeId
vendorSerial
}
deviceType {
productName
}
}
}
Error Handling
- Invalid CBOR - Malformed binary should be logged and discarded
- Missing required fields - Validate
tsmIdandtsmEvpresence - Out-of-range values - Check multiplier results are reasonable
- Compression errors - Try non-compressed decode if decompression fails
Performance Optimization
- Batch processing - Process messages in batches rather than one-by-one
- Cache mappings - Load CBOR index map once, reuse for all messages
- Parallel decoding - Use worker threads for high-volume deployments
- Database writes - Batch inserts for better throughput
Message ID Ranges
Message IDs (tsmId) identify the device type and message structure:
| Profile ID Range | Description | Example Devices |
|---|---|---|
| 1000-1999 | Common profile | System messages |
| 12000-12999 | Environment monitoring | Thingsee ENVIRONMENT |
| 13000-13999 | Presence and occupancy | Thingsee PRESENCE |
| 14000-14999 | Distance measurement | Thingsee COUNT, Thingsee BEAM |
| 28000-28999 | Radar-based sensors | Thingsee RADAR |
Refer to device-specific documentation for complete message ID lists and field definitions.
Troubleshooting
No Messages Received
Check:
- MQTT subscription includes correct Wirepas endpoint (21/21 for Thingsee CBOR)
- Gateway is connected and forwarding messages
- Sensors are configured with correct network ID and encryption keys
- Default report interval is often 1 hour - wait or configure shorter interval
CBOR Decoding Errors
Check:
- Binary payload is complete (not truncated)
- Using correct CBOR library version
- Message might be compressed - try zlib decompression first
- Endpoint 238/238 uses TLV, not CBOR
Incorrect Values After Conversion
Check:
- Multiplier applied correctly (divide by 10^x, not multiply)
- CBOR index mapping matches device firmware version
- Device-specific indexes (>1000) documented in device manual
- Negative values handled correctly (temperature, acceleration)
Missing Timestamp
Check:
- Gateway adding
tsmTsfrom Wirepas time-in-transit - Network disconnected events (tsmEv: 18) may require cloud-generated timestamp
- Clock synchronization on gateway
Unknown CBOR Indexes
Check:
- Device firmware version - new firmware may add fields
- Device-specific documentation for custom indexes
- Log unknown indexes to identify patterns
- Update decoder mapping table
Related Documentation
- Thingsee PRESENCE - Presence sensor message details
- Binary Passthrough API - Direct CBOR message access
- Service API (GraphQL) - Device identity lookup
- Message Profiles - Profile-specific message definitions