Hi guys,
I discovered nodered because I found 2 flows a guy kindly shared in a forum that does exactly what I need. So bear with me !
I installed nodered in my homeassistant instance (few days ago, so everything is up to date), I imported both his flows and I managed to get this working. I can tell because he included ingest nodes so we could troubleshoot and final data are sent to my endpoint.
Problem started when I tried to adapt his flows to my configuration.
The guy receives his input messages by connecting to SenseCAP cloud portal API, on the contrary, I own a gateway from this brand. This gateway has no API but it's configured to send data to my Home Assistant MQTT.
So I connected his flows to my MQTT and obviously, data are not formatted as the flow expects, so it fails. I've tried to understand the nodes and the guy's code but I'm no dev so I'm struggling.
I think I narrowed it to only 2 issues:
The guy can extract the "measureID" from the msg.topic but I can't. To try and fix this I replaced his code to get the measureId from the payload.......
var myArray = msg.topic.split("/");
msg.payload.measureId = msg.payload.object.messages[0][1].measurementId;
msg.payload.deviceId = myArray[3].toUpperCase();
return msg;
The flow goes on with my workaround but next failure comes when he process data in "Set data, considerIp and URL" node.
On his ingest node, data are at the same level:
{
"value": [
{
"mac": "1C:57:3E:39:47:50",
"rssi": "-71"
},
{
"mac": "D4:F8:29:B3:2B:30",
"rssi": "-90"
},
{
"mac": "98:25:4A:2D:B2:50",
"rssi": "-52"
},
{
"mac": "68:3F:7D:C9:08:40",
"rssi": "-70"
}
],
"timestamp": 1742821277000,
"measureId": "5001",
"deviceId": "2cg7f1c064900884"
}
In my MQTT message, data are spread in arrays or in the main structure of the message like so:
{
"deduplicationId": "58d3b1f7-5700-47fb-8629-a25cc0092476",
"time": "2025-03-24T14:01:20.019564078+00:00",
"deviceInfo": {
"tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242",
"tenantName": "SenseCAP",
"applicationId": "053edc9b-4c47-4a2f-b0e8-414dacbada7a",
"applicationName": "Home Assistant",
"deviceProfileId": "3c6aa0b4-11cd-4a2d-abaa-6b3eb3e7edbf",
"deviceProfileName": "Tracker",
"deviceName": "Tracker Anaïs",
"devEui": "2cf7f1c064900884",
"tags": {}
},
"devAddr": "01235505",
"adr": true,
"dr": 5,
"fCnt": 305,
"fPort": 5,
"confirmed": true,
"data": "BwAAAABn4WWvaD99yQhAvZglSi2yUNgYYiyj08Wl/////////wEAABVU",
"object": {
"messages": [
[
{
"motionId": 0,
"type": "Event Status",
"measurementValue": [],
"measurementId": "4200",
"timestamp": 1742824879000
},
{
"type": "Wi-Fi Scan",
"measurementId": "5001",
"measurementValue": [
{
"rssi": -67,
"mac": "68:3F:7D:C9:08:40"
},
{
"rssi": -40,
"mac": "98:25:4A:2D:B2:50"
},
{
"mac": "18:62:2C:A3:D3:C5",
"rssi": -91
}
],
"timestamp": 1742824879000,
"motionId": 0
},
{
"measurementId": "4097",
"measurementValue": 25.6,
"type": "Air Temperature",
"motionId": 0,
"timestamp": 1742824879000
},
{
"measurementValue": 21,
"motionId": 0,
"type": "Light",
"timestamp": 1742824879000,
"measurementId": "4199"
},
{
"motionId": 0,
"timestamp": 1742824879000,
"type": "Battery",
"measurementValue": 84,
"measurementId": "3000"
}
]
],
"valid": true,
"err": 0,
"payload": "070000000067e165af683f7dc90840bd98254a2db250d818622ca3d3c5a5ffffffffffffff0100001554"
},
"rxInfo": [
{
"gatewayId": "2cf7f110626000a2",
"uplinkId": 1227594873,
"rssi": -38,
"snr": 14.8,
"channel": 3,
"location": {
"latitude": 48.89110415553951,
"longitude": 2.113258838653565
},
"context": "2ZrkFA==",
"metadata": {
"region_common_name": "EU868",
"region_config_id": "EU_863_870_TTN"
},
"crcStatus": "CRC_OK"
}
],
"txInfo": {
"frequency": 867100000,
"modulation": {
"lora": {
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
}
}
}
}
Here is the first flow the guy shared:
[
{
"id": "55a7380fb743d666",
"type": "tab",
"label": "SenseCAP",
"disabled": false,
"info": ""
},
{
"id": "a452700489a0f506",
"type": "mqtt in",
"z": "55a7380fb743d666",
"name": "",
"topic": "/device_sensor_data/YOUR_ORGANIZATION_ID/+/1/+/+",
"qos": "2",
"datatype": "auto-detect",
"broker": "270bf66dda334b51",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 200,
"y": 100,
"wires": [
[
"c3bfe4d4202e4a13"
]
]
},
{
"id": "c3bfe4d4202e4a13",
"type": "function",
"z": "55a7380fb743d666",
"name": "set measureID & deviceId",
"func": "var myArray = msg.topic.split(\"/\");\nmsg.payload.measureId = myArray[6]; \nmsg.payload.deviceId = myArray[3].toUpperCase(); \nreturn msg;",
"outputs": 1,
"timeout": 0,
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 530,
"y": 100,
"wires": [
[
"9347ab5a76be7d81"
]
]
},
{
"id": "9347ab5a76be7d81",
"type": "switch",
"z": "55a7380fb743d666",
"name": "Lon - Lat - WiFi",
"property": "payload.measureId",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "4197",
"vt": "num"
},
{
"t": "eq",
"v": "4198",
"vt": "num"
},
{
"t": "eq",
"v": "5001",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 3,
"x": 760,
"y": 100,
"wires": [
[
"25c51ea149655734"
],
[
"1d11a59570af2a39"
],
[
"1d021e3fcec76e4a"
]
]
},
{
"id": "25c51ea149655734",
"type": "change",
"z": "55a7380fb743d666",
"name": "",
"rules": [
{
"t": "set",
"p": "parts.index",
"pt": "msg",
"to": "0",
"tot": "num"
},
{
"t": "set",
"p": "parts.count",
"pt": "msg",
"to": "2",
"tot": "num"
},
{
"t": "set",
"p": "parts.id",
"pt": "msg",
"to": "msg.payload.deviceId & msg.payload.timestamp",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 980,
"y": 40,
"wires": [
[
"c2bbb3ff2f6bb57f"
]
]
},
{
"id": "1d11a59570af2a39",
"type": "change",
"z": "55a7380fb743d666",
"name": "",
"rules": [
{
"t": "set",
"p": "parts.index",
"pt": "msg",
"to": "1",
"tot": "num"
},
{
"t": "set",
"p": "parts.count",
"pt": "msg",
"to": "2",
"tot": "num"
},
{
"t": "set",
"p": "parts.id",
"pt": "msg",
"to": "msg.payload.deviceId & msg.payload.timestamp",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 980,
"y": 100,
"wires": [
[
"c2bbb3ff2f6bb57f"
]
]
},
{
"id": "c2bbb3ff2f6bb57f",
"type": "join",
"z": "55a7380fb743d666",
"name": "",
"mode": "auto",
"build": "object",
"property": "payload",
"propertyType": "msg",
"key": "topic",
"joiner": "\\n",
"joinerType": "str",
"accumulate": true,
"timeout": "",
"count": "",
"reduceRight": false,
"reduceExp": "",
"reduceInit": "",
"reduceInitType": "",
"reduceFixup": "",
"x": 1150,
"y": 100,
"wires": [
[
"fe90207af88fc89e"
]
]
},
{
"id": "7d2b45e11dbefe50",
"type": "inject",
"z": "55a7380fb743d666",
"name": "Lon",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "/device_sensor_data/1111/ABCD/1/+/4197",
"payload": "{\"value\":71.41331,\"timestamp\":1700230308000,\"measureId\":\"4197\"}",
"payloadType": "json",
"x": 330,
"y": 40,
"wires": [
[
"c3bfe4d4202e4a13"
]
]
},
{
"id": "b22175ae4ea38b38",
"type": "inject",
"z": "55a7380fb743d666",
"name": "Lat",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "/device_sensor_data/1111/ABCD/1/+/4198",
"payload": "{\"value\":53.44913,\"timestamp\":1700230308000,\"measureId\":\"4198\"}",
"payloadType": "json",
"x": 330,
"y": 160,
"wires": [
[
"c3bfe4d4202e4a13"
]
]
},
{
"id": "2b368c70b7d7bbe5",
"type": "mqtt out",
"z": "55a7380fb743d666",
"name": "",
"topic": "/tracking/add",
"qos": "",
"retain": "",
"respTopic": "",
"contentType": "",
"userProps": "",
"correl": "",
"expiry": "",
"broker": "35fd3a139e459f2a",
"x": 2390,
"y": 240,
"wires": []
},
{
"id": "2b12b1b2616ad781",
"type": "http request",
"z": "55a7380fb743d666",
"name": "Get location from WiFi",
"method": "POST",
"ret": "obj",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 1040,
"y": 360,
"wires": [
[
"21c1ca15c0cdc4c3"
]
]
},
{
"id": "3cfcea9eb8d59444",
"type": "change",
"z": "55a7380fb743d666",
"name": "Replace property names",
"rules": [
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "mac",
"fromt": "str",
"to": "macAddress",
"tot": "str"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "rssi",
"fromt": "str",
"to": "signalStrength",
"tot": "str"
},
{
"t": "change",
"p": "payload",
"pt": "msg",
"from": "value",
"fromt": "str",
"to": "wifiAccessPoints",
"tot": "str"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 350,
"y": 280,
"wires": [
[
"8db6c0a63beeed03"
]
]
},
{
"id": "1d021e3fcec76e4a",
"type": "json",
"z": "55a7380fb743d666",
"name": "Convert to string",
"property": "payload",
"action": "str",
"pretty": false,
"x": 120,
"y": 280,
"wires": [
[
"3cfcea9eb8d59444"
]
]
},
{
"id": "8db6c0a63beeed03",
"type": "json",
"z": "55a7380fb743d666",
"name": "Convert to object",
"property": "payload",
"action": "obj",
"pretty": false,
"x": 590,
"y": 280,
"wires": [
[
"21a76a972e1ac698"
]
]
},
{
"id": "21a76a972e1ac698",
"type": "change",
"z": "55a7380fb743d666",
"name": "Set data, considerIp and URL",
"rules": [
{
"t": "move",
"p": "payload.timestamp",
"pt": "msg",
"to": "data.timestamp",
"tot": "msg"
},
{
"t": "move",
"p": "payload.deviceId",
"pt": "msg",
"to": "data.id",
"tot": "msg"
},
{
"t": "set",
"p": "payload.considerIp",
"pt": "msg",
"to": "false",
"tot": "bool"
},
{
"t": "set",
"p": "url",
"pt": "msg",
"to": "\"https://www.googleapis.com/geolocation/v1/geolocate?key=\" & $env('googleToken')\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 850,
"y": 280,
"wires": [
[
"2b12b1b2616ad781"
]
]
},
{
"id": "21c1ca15c0cdc4c3",
"type": "switch",
"z": "55a7380fb743d666",
"name": "Check accuracy",
"property": "payload.accuracy",
"propertyType": "msg",
"rules": [
{
"t": "lt",
"v": "50",
"vt": "num"
}
],
"checkall": "true",
"repair": false,
"outputs": 1,
"x": 1180,
"y": 280,
"wires": [
[
"a075c4ddced1bf40"
]
]
},
{
"id": "dc046029d6ff210b",
"type": "inject",
"z": "55a7380fb743d666",
"name": "WiFi",
"props": [
{
"p": "payload"
},
{
"p": "topic",
"vt": "str"
}
],
"repeat": "",
"crontab": "",
"once": false,
"onceDelay": 0.1,
"topic": "",
"payload": "{\"value\":[{\"mac\":\"00:00:00:00:00:00\",\"rssi\":\"-91\"},{\"mac\":\"00:00:00:00:00:00\",\"rssi\":\"-90\"},{\"mac\":\"00:00:00:00:00:00\",\"rssi\":\"-86\"},{\"mac\":\"00:00:00:00:00:00\",\"rssi\":\"-74\"}],\"timestamp\":1700481455000,\"measureId\":\"5001\",\"deviceId\":\"ABCD\"}",
"payloadType": "json",
"x": 570,
"y": 40,
"wires": [
[
"9347ab5a76be7d81"
]
]
},
{
"id": "3b73d15afdacc315",
"type": "change",
"z": "55a7380fb743d666",
"name": "Set URL",
"rules": [
{
"t": "set",
"p": "url",
"pt": "msg",
"to": "\"https://maps.googleapis.com/maps/api/elevation/json?locations=\" & msg.data.lat & \",\" & msg.data.lon & \"&key=\" & $env('googleToken')\t",
"tot": "jsonata"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1660,
"y": 180,
"wires": [
[
"1ae88917e744764d"
]
]
},
{
"id": "1ae88917e744764d",
"type": "http request",
"z": "55a7380fb743d666",
"name": "Get elevation",
"method": "GET",
"ret": "obj",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 1770,
"y": 260,
"wires": [
[
"8eb4e0b032497b4f"
]
]
},
{
"id": "fe90207af88fc89e",
"type": "change",
"z": "55a7380fb743d666",
"name": "Prepare MQTT GPS data",
"rules": [
{
"t": "move",
"p": "payload[0].value",
"pt": "msg",
"to": "data.lon",
"tot": "msg"
},
{
"t": "move",
"p": "payload[1].value",
"pt": "msg",
"to": "data.lat",
"tot": "msg"
},
{
"t": "move",
"p": "payload[0].timestamp",
"pt": "msg",
"to": "data.timestamp",
"tot": "msg"
},
{
"t": "move",
"p": "payload[0].deviceId",
"pt": "msg",
"to": "data.id",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1370,
"y": 100,
"wires": [
[
"3b73d15afdacc315"
]
]
},
{
"id": "a075c4ddced1bf40",
"type": "change",
"z": "55a7380fb743d666",
"name": "Prepare MQTT WiFI data",
"rules": [
{
"t": "move",
"p": "payload.location.lng",
"pt": "msg",
"to": "data.lon",
"tot": "msg"
},
{
"t": "move",
"p": "payload.location.lat",
"pt": "msg",
"to": "data.lat",
"tot": "msg"
},
{
"t": "set",
"p": "data.hdop",
"pt": "msg",
"to": "1000",
"tot": "num"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 1410,
"y": 280,
"wires": [
[
"3b73d15afdacc315"
]
]
},
{
"id": "8eb4e0b032497b4f",
"type": "switch",
"z": "55a7380fb743d666",
"name": "",
"property": "statusCode",
"propertyType": "msg",
"rules": [
{
"t": "eq",
"v": "200",
"vt": "num"
},
{
"t": "else"
}
],
"checkall": "true",
"repair": false,
"outputs": 2,
"x": 1870,
"y": 180,
"wires": [
[
"72e902c74e616e1b"
],
[
"35eb4ab3ebe47a3b"
]
]
},
{
"id": "72e902c74e616e1b",
"type": "change",
"z": "55a7380fb743d666",
"name": "Set altitude",
"rules": [
{
"t": "set",
"p": "data.altitude",
"pt": "msg",
"to": "payload.results[0].elevation",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 2050,
"y": 120,
"wires": [
[
"35eb4ab3ebe47a3b"
]
]
},
{
"id": "35eb4ab3ebe47a3b",
"type": "change",
"z": "55a7380fb743d666",
"name": "Move data to payload",
"rules": [
{
"t": "move",
"p": "data",
"pt": "msg",
"to": "payload",
"tot": "msg"
}
],
"action": "",
"property": "",
"from": "",
"to": "",
"reg": false,
"x": 2180,
"y": 240,
"wires": [
[
"2b368c70b7d7bbe5"
]
]
},
{
"id": "270bf66dda334b51",
"type": "mqtt-broker",
"name": "SenseCAP",
"broker": "sensecap-openstream.seeed.cc",
"port": "1883",
"clientid": "YOUR_CLIENTID",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"autoUnsubscribe": true,
"birthTopic": "",
"birthQos": "0",
"birthRetain": "false",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closeRetain": "false",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willRetain": "false",
"willPayload": "",
"willMsg": {},
"userProps": "",
"sessionExpiry": ""
},
{
"id": "35fd3a139e459f2a",
"type": "mqtt-broker",
"name": "YOUR_MQTT_SERVER_NAME",
"broker": "1YOUR_MQTT_SERVER_URL",
"port": "YOUR_MQTT_SERVER_PORT",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"sessionExpiry": ""
}
]
Here is the second flow:
[
{
"id": "4e134a97278a50c7",
"type": "http request",
"z": "1663510036786436",
"name": "Create postion in Traccar",
"method": "GET",
"ret": "txt",
"paytoqs": "ignore",
"url": "",
"tls": "",
"persist": false,
"proxy": "",
"insecureHTTPParser": false,
"authType": "",
"senderr": false,
"headers": [],
"x": 470,
"y": 920,
"wires": [
[]
]
},
{
"id": "e943624eef5477a5",
"type": "mqtt in",
"z": "1663510036786436",
"name": "",
"topic": "/tracking/add",
"qos": "2",
"datatype": "auto-detect",
"broker": "35fd3a139e459f2a",
"nl": false,
"rap": true,
"rh": 0,
"inputs": 0,
"x": 90,
"y": 920,
"wires": [
[
"620c805b0ccba766"
]
]
},
{
"id": "620c805b0ccba766",
"type": "function",
"z": "1663510036786436",
"name": "Set URL",
"func": "msg.url=env.get(\"host\")+\":\"+env.get(\"portOsmand\")+\"/?id=\"+msg.payload.id+\"&lat=\"+msg.payload.lat+\"&lon=\"+msg.payload.lon+\"×tamp=\"+msg.payload.timestamp\n\nif (msg.payload.speed != null)\n{\n msg.url = msg.url + \"&speed=\" + msg.payload.speed\n}\n\nif (msg.payload.altitude != null)\n{\n msg.url = msg.url + \"&altitude=\" + msg.payload.altitude\n}\n\nif (msg.payload.batt != null)\n{\n msg.url = msg.url + \"&batt=\" + msg.payload.batt\n}\n\nif (msg.payload.hdop != null)\n{\n msg.url = msg.url + \"&hdop=\" + msg.payload.hdop\n}\n\nreturn msg;",
"outputs": 1,
"timeout": "",
"noerr": 0,
"initialize": "",
"finalize": "",
"libs": [],
"x": 260,
"y": 920,
"wires": [
[
"4e134a97278a50c7"
]
]
},
{
"id": "35fd3a139e459f2a",
"type": "mqtt-broker",
"name": "YOUR_MQTT_SERVER_NAME",
"broker": "1YOUR_MQTT_SERVER_URL",
"port": "YOUR_MQTT_SERVER_PORT",
"clientid": "",
"autoConnect": true,
"usetls": false,
"protocolVersion": "4",
"keepalive": "60",
"cleansession": true,
"birthTopic": "",
"birthQos": "0",
"birthPayload": "",
"birthMsg": {},
"closeTopic": "",
"closeQos": "0",
"closePayload": "",
"closeMsg": {},
"willTopic": "",
"willQos": "0",
"willPayload": "",
"willMsg": {},
"sessionExpiry": ""
}
]
I tried split, join, change nodes but as I don't understand what I do, I could try for few years more without succeeding and I'm loosing faith. so I'm asking help here because all this is way beyond my pay grade.
Thanks for your help.
Arnaud