diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/converter.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/converter.json index c6c4aeaf..3d8b0716 100644 --- a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/converter.json +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Bosch parking lot sensor", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735038404143 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"Bosch parking lot sensor\" + data.EUI;\nvar deviceType = \"Parking sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.port;\n\n if (fPort === 1) { // Parking status\n decoded.type = \"parking status\";\n decoded.occupied = (input[0] & 0x1) === 0x1;\n\n } else if (fPort === 2) { // Heartbeat\n decoded.type = \"heartbeat\";\n decoded.occupied = (input[0] & 0x1) === 0x1;\n if (input.length >= 2) {\n var temperature = input[1];\n decoded.temperature = (temperature & 0x80) != 0 ? temperature - 0x100 : temperature;\n }\n } else if (fPort === 3) { // Start-up\n decoded.type = \"startup\";\n decoded.occupied = (input[16] & 0x1) === 0x1;\n\n var resetCause = input[15];\n if (resetCause === 0x01) {\n decoded.resetCause = \"Watchdog reset\";\n } else if (resetCause === 0x02) {\n decoded.resetCause = \"Power On Reset\";\n } else if (resetCause === 0x03) {\n decoded.resetCause = \"System Request Reset\";\n } else if (resetCause === 0x04) {\n decoded.resetCause = \"External Pin Reset\";\n } else if (resetCause === 0x05) {\n decoded.resetCause = \"Lockup Reset\";\n } else if (resetCause === 0x06) {\n decoded.resetCause = \"Brownout Reset\";\n } else {\n decoded.resetCause = \"Unknown\";\n }\n\n decoded.firmwareVersion = input[14] + \".\" + input[13] + \".\" + input[12];\n var debugInfo = \"\";\n for (int i = 0; i < 12; i++) {\n debugInfo += String.format(\"%02x\", input[i]);\n if (i < 11) {\n debugInfo += \" \";\n }\n } \n decoded.debugInfo = debugInfo;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"Bosch parking lot sensor \" + data.EUI;\nvar deviceType = \"Parking sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.port;\n\n if (fPort === 1) { // Parking status\n decoded.type = \"parking status\";\n decoded.occupied = (input[0] & 0x1) === 0x1;\n\n } else if (fPort === 2) { // Heartbeat\n decoded.type = \"heartbeat\";\n decoded.occupied = (input[0] & 0x1) === 0x1;\n if (input.length >= 2) {\n var temperature = input[1];\n decoded.temperature = (temperature & 0x80) != 0 ? temperature - 0x100 : temperature;\n }\n } else if (fPort === 3) { // Start-up\n decoded.type = \"startup\";\n decoded.occupied = (input[16] & 0x1) === 0x1;\n\n var resetCause = input[15];\n if (resetCause === 0x01) {\n decoded.resetCause = \"Watchdog reset\";\n } else if (resetCause === 0x02) {\n decoded.resetCause = \"Power On Reset\";\n } else if (resetCause === 0x03) {\n decoded.resetCause = \"System Request Reset\";\n } else if (resetCause === 0x04) {\n decoded.resetCause = \"External Pin Reset\";\n } else if (resetCause === 0x05) {\n decoded.resetCause = \"Lockup Reset\";\n } else if (resetCause === 0x06) {\n decoded.resetCause = \"Brownout Reset\";\n } else {\n decoded.resetCause = \"Unknown\";\n }\n\n decoded.firmwareVersion = input[14] + \".\" + input[13] + \".\" + input[12];\n var debugInfo = \"\";\n for (int i = 0; i < 12; i++) {\n debugInfo += String.format(\"%02x\", input[i]);\n if (i < 11) {\n debugInfo += \" \";\n }\n } \n decoded.debugInfo = debugInfo;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/payload_3.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..6f629f5b --- /dev/null +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 2, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0112" +} \ No newline at end of file diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result.json index ec7b80b0..9d1a5eed 100644 --- a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result.json +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result.json @@ -1,5 +1,5 @@ [{ - "deviceName": "Bosch parking lot sensor1000000000000001", + "deviceName": "Bosch parking lot sensor 1000000000000001", "deviceType": "Parking sensor", "attributes": { "eui": "1000000000000001", diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_1.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_1.json index ebed3a17..8edf9472 100644 --- a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_1.json +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_1.json @@ -1,5 +1,5 @@ [{ - "deviceName": "Bosch parking lot sensor1000000000000001", + "deviceName": "Bosch parking lot sensor 1000000000000001", "deviceType": "Parking sensor", "attributes": { "eui": "1000000000000001", diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_2.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_2.json index 31b7be5e..0f734a8a 100644 --- a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_2.json +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_2.json @@ -1,5 +1,5 @@ [{ - "deviceName": "Bosch parking lot sensor1000000000000001", + "deviceName": "Bosch parking lot sensor 1000000000000001", "deviceType": "Parking sensor", "attributes": { "eui": "1000000000000001", diff --git a/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_3.json b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..603f4248 --- /dev/null +++ b/VENDORS/Bosch/Parking_Lot_Sensor/LORIOT/uplink/result_3.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "Bosch parking lot sensor 0102030405060708", + "deviceType": "Parking sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 2, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "type": "heartbeat", + "occupied": true, + "temperature": 18 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Bosch/logo.svg b/VENDORS/Bosch/logo.svg index 3ff8dbb7..33b82f41 100644 --- a/VENDORS/Bosch/logo.svg +++ b/VENDORS/Bosch/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4bf804b4 --- /dev/null +++ b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02023b0003003702710c60" +} diff --git a/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..5c37022a --- /dev/null +++ b/VENDORS/Decentlab/DL-5TM/LORIOT/uplink/result_2.json @@ -0,0 +1,21 @@ +{ + "deviceName": "571", + "deviceType": "DL-5TM", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "dielectric_permittivity": 1.1, + "volumetric_water_content": -0.0215397767, + "soil_temperature": 22.5, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4bf804b4 --- /dev/null +++ b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02023b0003003702710c60" +} diff --git a/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..70a51ba0 --- /dev/null +++ b/VENDORS/Decentlab/DL-ALB/LORIOT/uplink/result_2.json @@ -0,0 +1,21 @@ +{ + "deviceName": "571", + "deviceType": "DL-ALB", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "incoming_radiation": -3271.3, + "reflected_radiation": -3214.3, + "albedo": 0, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..8d99f6d1 --- /dev/null +++ b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0208c900020bf5" +} diff --git a/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..590c0fce --- /dev/null +++ b/VENDORS/Decentlab/DL-ATM22/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "2249", + "deviceType": "DL-ATM22", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.061, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..a13c34a3 --- /dev/null +++ b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02035a0003800a8000800080008009812b8014810880b4a57c820c810980027fe88056800880040bf5" +} diff --git a/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..cbddab65 --- /dev/null +++ b/VENDORS/Decentlab/DL-ATM41/LORIOT/uplink/result_2.json @@ -0,0 +1,35 @@ +{ + "deviceName": "858", + "deviceType": "DL-ATM41", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "solar_radiation": 10, + "precipitation": 0, + "lightning_strike_count": 0, + "lightning_average_distance": 0, + "wind_speed": 0.09, + "wind_direction": 29.9, + "maximum_wind_speed": 0.2, + "air_temperature": 26.4, + "vapor_pressure": 1.8, + "atmospheric_pressure": 95.96, + "relative_humidity": 52.4, + "sensor_temperature_internal": 26.5, + "x_orientation_angle": 0.2, + "y_orientation_angle": -2.4, + "compass_heading": 86, + "north_wind_speed": 0.08, + "east_wind_speed": 0.04, + "battery_voltage": 3.061, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..eb615d43 --- /dev/null +++ b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0230c50003a40c00810c60" +} diff --git a/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..0fce6e05 --- /dev/null +++ b/VENDORS/Decentlab/DL-BLG/LORIOT/uplink/result_2.json @@ -0,0 +1,21 @@ +{ + "deviceName": "12485", + "deviceType": "DL-BLG", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "voltage_ratio": 0.006409406661987305, + "thermistor_resistance": 115020.68221552655, + "temperature": 22.028848392450755, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..14525c19 --- /dev/null +++ b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0207d900020c60" +} diff --git a/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..f0fb8977 --- /dev/null +++ b/VENDORS/Decentlab/DL-CTD10/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "2009", + "deviceType": "DL-CTD10", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..d9ee4eb3 --- /dev/null +++ b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02463900038a778a95977a874c80478a5e0b74" +} diff --git a/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..ea4273e2 --- /dev/null +++ b/VENDORS/Decentlab/DL-CWS/LORIOT/uplink/result_2.json @@ -0,0 +1,24 @@ +{ + "deviceName": "17977", + "deviceType": "DL-CWS", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "surface_temperature": 26.79, + "air_temperature": 27.09, + "air_humidity": 60.1, + "dew_point": 18.68, + "angle": 71, + "sensor_temperature": 26.54, + "battery_voltage": 2.932, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..3aeb0eb2 --- /dev/null +++ b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0258c000074676fa0a81c9813fa6d88137802581300b91" +} diff --git a/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..c3b8d794 --- /dev/null +++ b/VENDORS/Decentlab/DL-CWS2/LORIOT/uplink/result_2.json @@ -0,0 +1,26 @@ +{ + "deviceName": "22720", + "deviceType": "DL-CWS2", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "air_temperature_radiation_shield": 3.167391470206759, + "air_humidity_radiation_shield": 97.67299916075379, + "surface_temperature": 4.57, + "air_temperature": 3.19, + "air_humidity": 99.44, + "dew_point": 3.11, + "angle": 37, + "sensor_temperature": 3.04, + "battery_voltage": 2.961, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/payload_1.json b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..6475bb71 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02199e000300000258000000000c9b" +} diff --git a/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/result_1.json b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..9f235539 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-002/LORIOT/uplink/result_1.json @@ -0,0 +1,21 @@ +{ + "deviceName": "6558", + "deviceType": "DL-DLR2-002", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pulse_count": 0, + "pulse_interval": 600, + "cumulative_pulse_count": 0, + "battery_voltage": 3.227, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/payload_1.json b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..6475bb71 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02199e000300000258000000000c9b" +} diff --git a/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/result_1.json b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..dfa3dab0 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-003/LORIOT/uplink/result_1.json @@ -0,0 +1,19 @@ +{ + "deviceName": "6558", + "deviceType": "DL-DLR2-003", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "input": 0, + "battery_voltage": 0.6, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..9956fb23 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0208b200038bb80c60" +} diff --git a/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..208f668e --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-004/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "2226", + "deviceType": "DL-DLR2-004", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "current": 13.73291015625, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..b32b3721 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02113a00020c6d" +} diff --git a/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..4c3265a7 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-005/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "4410", + "deviceType": "DL-DLR2-005", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.181, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..3c69bb97 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02111100020c54" +} diff --git a/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..ca66a1d8 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-006/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "4369", + "deviceType": "DL-DLR2-006", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..b215d68d --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0207df000317de008d0c60" +} diff --git a/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..0f67e61b --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-008/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "2015", + "deviceType": "DL-DLR2-008", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 20.031064968287208, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..20d05d94 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "024c620003573400ad0ae1" +} diff --git a/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..b552e94d --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-009/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "19554", + "deviceType": "DL-DLR2-009", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "thermistor_resistance": 1097.0478279778865, + "temperature": 24.908127512458456, + "battery_voltage": 2.785, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..3ee3ac17 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02198f0007000402580bf0000100000258dece00000c33" +} diff --git a/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..46d7387f --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-010/LORIOT/uplink/result_2.json @@ -0,0 +1,24 @@ +{ + "deviceName": "6543", + "deviceType": "DL-DLR2-010", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "ch0_pulse_count": 4, + "ch0_pulse_interval": 600, + "ch0_cumulative_pulse_count": 68592, + "ch1_pulse_count": 0, + "ch1_pulse_interval": 600, + "ch1_cumulative_pulse_count": 57038, + "battery_voltage": 3.123, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..43ef14bb --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02437100040af9" +} diff --git a/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..c5d01b7a --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-011/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "17265", + "deviceType": "DL-DLR2-011", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 2.809, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4475d785 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0217830003162e00870c33" +} diff --git a/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3c8fb159 --- /dev/null +++ b/VENDORS/Decentlab/DL-DLR2-012/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "6019", + "deviceType": "DL-DLR2-012", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "strain_gauge": 1713.0065082323433, + "battery_voltage": 3.123, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..a9a9379b --- /dev/null +++ b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02023d0003815e0c15" +} diff --git a/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..02fc1e8c --- /dev/null +++ b/VENDORS/Decentlab/DL-DS18/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "573", + "deviceType": "DL-DS18", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 21.875, + "battery_voltage": 3.093, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..b447fdaf --- /dev/null +++ b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0203d400033bf67fff3bf60c60" +} diff --git a/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..7163580e --- /dev/null +++ b/VENDORS/Decentlab/DL-DWS/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "980", + "deviceType": "DL-DWS", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "frequency": 15350.468459120457, + "weight": 2390.4101368512333, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..69524752 --- /dev/null +++ b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02532b00038726892081148297a57380cf81700bbc" +} diff --git a/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..87c3a449 --- /dev/null +++ b/VENDORS/Decentlab/DL-GMM/LORIOT/uplink/result_2.json @@ -0,0 +1,25 @@ +{ + "deviceName": "21291", + "deviceType": "DL-GMM", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "photosynthetically_active_radiation": 183, + "air_temperature": 23.36, + "air_humidity": 27.6, + "co2_concentration": 663, + "atmospheric_pressure": 95.87, + "vapor_pressure_deficit": 2.07, + "dew_point": 3.68, + "battery_voltage": 3.004, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/payload_3.json b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..4a961eea --- /dev/null +++ b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "020bbd007f0b926a515d48bc4e0262006981c7000093d4000b0111" +} diff --git a/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/result_3.json b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..ec16c066 --- /dev/null +++ b/VENDORS/Decentlab/DL-IAM/LORIOT/uplink/result_3.json @@ -0,0 +1,29 @@ +{ + "deviceName": "3005", + "deviceType": "DL-IAM", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 2.962, + "air_temperature": 27.67833981841764, + "air_humidity": 36.438544289311054, + "barometric_pressure": 96412, + "ambient_light_visible_infrared": 610, + "ambient_light_infrared": 105, + "illuminance": 678.76512, + "co2_concentration": 455, + "co2_sensor_status": 0, + "raw_ir_reading": 37844, + "activity_counter": 11, + "total_voc": 273, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..5032c69e --- /dev/null +++ b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0256dc000302bc0c48" +} diff --git a/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..196af2b4 --- /dev/null +++ b/VENDORS/Decentlab/DL-IFD/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "22236", + "deviceType": "DL-IFD", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "fruit_size": 7, + "battery_voltage": 3.144, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..402f07b0 --- /dev/null +++ b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0256d200020c9d" +} diff --git a/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..f64270d6 --- /dev/null +++ b/VENDORS/Decentlab/DL-ILT/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "22226", + "deviceType": "DL-ILT", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.229, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..3cb2a33e --- /dev/null +++ b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0256e100020c52" +} diff --git a/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..4e253763 --- /dev/null +++ b/VENDORS/Decentlab/DL-ISD/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "22241", + "deviceType": "DL-ISD", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.154, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..745702c7 --- /dev/null +++ b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "023d0100030c290bab0c3e79707a1d78437991490845997e4cacdeaa6e00000000457e415a0b59" +} diff --git a/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..d7707c9d --- /dev/null +++ b/VENDORS/Decentlab/DL-ISF/LORIOT/uplink/result_2.json @@ -0,0 +1,33 @@ +{ + "deviceName": "15617", + "deviceType": "DL-ISF", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "sap_flow": -0.192, + "heat_velocity_outer": -2.208, + "heat_velocity_inner": 0.144, + "alpha_outer": -0.05184, + "alpha_inner": 0.00352, + "beta_outer": -0.14816, + "beta_inner": -0.04128, + "tmax_outer": 37.392, + "tmax_inner": 35.634, + "temperature_outer": -4.36, + "max_voltage": 11.486, + "min_voltage": 10.862, + "diagnostic": 0, + "upstream_tmax_outer": 35.58, + "upstream_tmax_inner": 33.46, + "battery_voltage": 2.905, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..9669c7e9 --- /dev/null +++ b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0202d9000304f904c40c54" +} diff --git a/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..c665ee12 --- /dev/null +++ b/VENDORS/Decentlab/DL-ITST/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "729", + "deviceType": "DL-ITST", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature_target": 27.3, + "temperature_head": 22, + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..b447fdaf --- /dev/null +++ b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0203d400033bf67fff3bf60c60" +} diff --git a/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..9ef7e3c0 --- /dev/null +++ b/VENDORS/Decentlab/DL-KL66/LORIOT/uplink/result_2.json @@ -0,0 +1,24 @@ +{ + "deviceName": "980", + "deviceType": "DL-KL66", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "counter_reading": 15350, + "measurement_interval": 0.999969482421875, + "frequency": 15350.468459120457, + "weight": -47.5066896399347, + "elongation": 0.6988257799379214, + "strain": 10.588269392998809, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LID/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..d6b0811c --- /dev/null +++ b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0211c90003119b117611bc119e118a119411a811a81194006401990abd" +} diff --git a/VENDORS/Decentlab/DL-LID/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..7ca9f991 --- /dev/null +++ b/VENDORS/Decentlab/DL-LID/LORIOT/uplink/result_2.json @@ -0,0 +1,29 @@ +{ + "deviceName": "4553", + "deviceType": "DL-LID", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "distance_average": 4507, + "distance_minimum": 4470, + "distance_maximum": 4540, + "distance_median": 4510, + "distance_10th_percentile": 4490, + "distance_25th_percentile": 4500, + "distance_75th_percentile": 4520, + "distance_90th_percentile": 4520, + "distance_most_frequent_value": 4500, + "number_of_samples": 100, + "total_acquisition_time": 399.4140625, + "battery_voltage": 2.749, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/payload_3.json b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..c6990039 --- /dev/null +++ b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "020578000f67bd618d1cedbd1081d981f4895b0bd80bb50000959895390c25" +} diff --git a/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/result_3.json b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..6f84f91f --- /dev/null +++ b/VENDORS/Decentlab/DL-LP8P/LORIOT/uplink/result_3.json @@ -0,0 +1,30 @@ +{ + "deviceName": "1400", + "deviceType": "DL-LP8P", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "air_temperature": 24.35660461425781, + "air_humidity": 41.63221740722656, + "barometer_temperature": 24.05, + "barometric_pressure": 96800, + "co2_concentration": 473, + "co2_concentration_lpf": 500, + "co2_sensor_temperature": 23.95, + "capacitor_voltage_1": 3.032, + "capacitor_voltage_2": 2.997, + "co2_sensor_status": 0, + "raw_ir_reading": 38296, + "raw_ir_reading_lpf": 38201, + "battery_voltage": 3.109, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4b89cdb2 --- /dev/null +++ b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0211110003409a00860c54" +} diff --git a/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..d3d8057d --- /dev/null +++ b/VENDORS/Decentlab/DL-LPW/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "4369", + "deviceType": "DL-LPW", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "potentiometer_position": 4.884648323059082, + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4b89cdb2 --- /dev/null +++ b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0211110003409a00860c54" +} diff --git a/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..c8f00b86 --- /dev/null +++ b/VENDORS/Decentlab/DL-LWS/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "4369", + "deviceType": "DL-LWS", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "leaf_wetness_index": 0.04884648323059082, + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/payload_3.json b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..b96f24a2 --- /dev/null +++ b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02012f000304d200010bb1" +} diff --git a/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/result_3.json b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..a3287c76 --- /dev/null +++ b/VENDORS/Decentlab/DL-MBX/LORIOT/uplink/result_3.json @@ -0,0 +1,20 @@ +{ + "deviceName": "303", + "deviceType": "DL-MBX", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "distance": 1234, + "number_of_valid_samples": 1, + "battery_voltage": 2.993, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..6cdb51e9 --- /dev/null +++ b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "024E030003000088e6210800f223650af5" +} diff --git a/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..570ea0bd --- /dev/null +++ b/VENDORS/Decentlab/DL-MES5/LORIOT/uplink/result_2.json @@ -0,0 +1,23 @@ +{ + "deviceName": "19971", + "deviceType": "DL-MES5", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "status": 0, + "temperature": 22.78, + "sludge_blanket": 84.56, + "suspended_solid": 2.42, + "turbidity": 906.1, + "battery_voltage": 2.805, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..eea84c28 --- /dev/null +++ b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "022332000300008885013e013e02330b10" +} diff --git a/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..8f169ed5 --- /dev/null +++ b/VENDORS/Decentlab/DL-NTU/LORIOT/uplink/result_2.json @@ -0,0 +1,23 @@ +{ + "deviceName": "9010", + "deviceType": "DL-NTU", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "status": 0, + "temperature": 21.81, + "turbidity_in_ntu": 31.8, + "turbidity_in_fnu": 31.8, + "turbidity_in_mg_l": 56.3, + "battery_voltage": 2.832, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..cd0813f3 --- /dev/null +++ b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02186c000300008862a618836583650c60" +} diff --git a/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..ebfd8391 --- /dev/null +++ b/VENDORS/Decentlab/DL-OPTOD/LORIOT/uplink/result_2.json @@ -0,0 +1,23 @@ +{ + "deviceName": "6252", + "deviceType": "DL-OPTOD", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "status": 0, + "temperature": 21.46, + "oxygen_saturation": 97.52, + "oxygen_concentration": 8.69, + "oxygen_concentration_alt": 8.69, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..8c894b04 --- /dev/null +++ b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "020291000380690c60" +} diff --git a/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..31fe9462 --- /dev/null +++ b/VENDORS/Decentlab/DL-PAR/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "657", + "deviceType": "DL-PAR", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "photosynthetically_active_radiation": 48.065185546875, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..eeec5e5e --- /dev/null +++ b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0252b800030000884282c77f637ff60c5c" +} diff --git a/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..d6f17592 --- /dev/null +++ b/VENDORS/Decentlab/DL-PHEHT/LORIOT/uplink/result_2.json @@ -0,0 +1,23 @@ +{ + "deviceName": "21176", + "deviceType": "DL-PHEHT", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "status": 0, + "temperature": 21.14, + "ph": 7.11, + "redox": -15.7, + "ph_mv": -1, + "battery_voltage": 3.164, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PM/LORIOT/uplink/payload_3.json b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..81ce8c77 --- /dev/null +++ b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "021b50000f0c25002500270027002701f50107012c012d012d012d67bd618dbd10" +} diff --git a/VENDORS/Decentlab/DL-PM/LORIOT/uplink/result_3.json b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..7a507604 --- /dev/null +++ b/VENDORS/Decentlab/DL-PM/LORIOT/uplink/result_3.json @@ -0,0 +1,31 @@ +{ + "deviceName": "6992", + "deviceType": "DL-PM", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.109, + "pm1_0_mass_concentration": 3.7, + "pm2_5_mass_concentration": 3.9, + "pm4_mass_concentration": 3.9, + "pm10_mass_concentration": 3.9, + "typical_particle_size": 501, + "pm0_5_number_concentration": 26.3, + "pm1_0_number_concentration": 30, + "pm2_5_number_concentration": 30.1, + "pm4_number_concentration": 30.1, + "pm10_number_concentration": 30.1, + "air_temperature": 24.35660461425781, + "air_humidity": 41.63221740722656, + "barometric_pressure": 96800, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..1e56c809 --- /dev/null +++ b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02016700034e8060170c7f" +} diff --git a/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3902edae --- /dev/null +++ b/VENDORS/Decentlab/DL-PR21/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "359", + "deviceType": "DL-PR21", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pressure": 0.24609375, + "temperature": 25.671875, + "battery_voltage": 3.199, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..5c952029 --- /dev/null +++ b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02016700033e8060170c7f" +} diff --git a/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..46605f9b --- /dev/null +++ b/VENDORS/Decentlab/DL-PR26/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "359", + "deviceType": "DL-PR26", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pressure": -0.01171875, + "temperature": 25.671875, + "battery_voltage": 3.199, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..feffc7ad --- /dev/null +++ b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02032b0003806797810c2b" +} diff --git a/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..9db74732 --- /dev/null +++ b/VENDORS/Decentlab/DL-PR36/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "811", + "deviceType": "DL-PR36", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pressure": 0.0125732421875, + "temperature": 23.50390625, + "battery_voltage": 3.115, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4923774e --- /dev/null +++ b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "020a17000380079786978180060c2b" +} diff --git a/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..1a488fc1 --- /dev/null +++ b/VENDORS/Decentlab/DL-PR36CTD/LORIOT/uplink/result_2.json @@ -0,0 +1,22 @@ +{ + "deviceName": "2583", + "deviceType": "DL-PR36CTD", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pressure": 0.0008544921875, + "temperature_electronics": 23.5234375, + "temperature_pt1000": 23.50390625, + "electrical_conductivity": 0.005859375, + "battery_voltage": 3.115, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..d574dc87 --- /dev/null +++ b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02029100020c60" +} diff --git a/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..4e95d5e3 --- /dev/null +++ b/VENDORS/Decentlab/DL-PYR/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "657", + "deviceType": "DL-PYR", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/payload_1.json b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..6a57849e --- /dev/null +++ b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0202e00003a9fd01341ca285f30c60" +} diff --git a/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/result_1.json b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..41a84a00 --- /dev/null +++ b/VENDORS/Decentlab/DL-RHC/LORIOT/uplink/result_1.json @@ -0,0 +1,21 @@ +{ + "deviceName": "736", + "deviceType": "DL-RHC", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "sensor_id": 20228605, + "air_humidity": 73.3, + "air_temperature": 15.23, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..f93162e5 --- /dev/null +++ b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0243e300058000800080008000800080008741877b8749876c876c876600000000000000000000014a09e3" +} diff --git a/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..a2349d58 --- /dev/null +++ b/VENDORS/Decentlab/DL-SDD/LORIOT/uplink/result_2.json @@ -0,0 +1,36 @@ +{ + "deviceName": "17379", + "deviceType": "DL-SDD", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "moisture_at_level_0": 0, + "moisture_at_level_1": 0, + "moisture_at_level_2": 0, + "moisture_at_level_3": 0, + "moisture_at_level_4": 0, + "moisture_at_level_5": 0, + "temperature_at_level_0": 18.57, + "temperature_at_level_1": 19.15, + "temperature_at_level_2": 18.65, + "temperature_at_level_3": 19, + "temperature_at_level_4": 19, + "temperature_at_level_5": 18.94, + "salinity_at_level_0": -100, + "salinity_at_level_1": -100, + "salinity_at_level_2": -100, + "salinity_at_level_3": -100, + "salinity_at_level_4": -100, + "salinity_at_level_5": 230, + "battery_voltage": 2.531, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json index f4d2c4ed..b5df8e83 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result.json index 4fba4c87..a1f6ddd0 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0003BB8", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_01.json index 3fa3482f..911cb1f3 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0003BB8", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_02.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_02.json index 32566044..46072cb4 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_02.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_02.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "0202020202020202", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_03.json b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_03.json index 1d189d1f..69ad5abe 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_03.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ChirpStack/uplink/result_03.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "0202020202020202", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json index be781ef1..4b7c01d4 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..dbd5395a --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02030e000364a079b10c60" +} diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result.json index 178e03a5..cffecb65 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "0004A30B001ADCB5", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_01.json index f531f446..ba31c58b 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "0004A30B001ADCB5", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..991fde36 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-001/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-001", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json index 2feb45d4..d03547dd 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result.json index 196a5cdf..3e9504d7 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA000156B", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result_01.json index e83504f1..ea5af45d 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingPark/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA000156B", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json index 533f0957..86f7f62b 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result.json index 196a5cdf..3e9504d7 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA000156B", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result_01.json index e83504f1..ea5af45d 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingParkEnterprise/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA000156B", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json index 5a1a4894..8871ef4b 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result.json index 2c54fb05..556d71b8 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0000BCE", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result_01.json index 17298040..c7da9207 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackCommunity/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0000BCE", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json index 0f49c3d4..02223686 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/converter.json @@ -14,6 +14,6 @@ "lora_dev_eui", "lora_snr" ], - "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-001',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" } } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result.json index 2c54fb05..556d71b8 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0000BCE", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result_01.json index 17298040..c7da9207 100644 --- a/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result_01.json +++ b/VENDORS/Decentlab/DL-SHT35-001/ThingsStackIndustries/uplink/result_01.json @@ -1,6 +1,6 @@ { "deviceName": "782", - "deviceType": "DL-SHT35", + "deviceType": "DL-SHT35-001", "attributes": { "lora_dev_eui": "70B3D57BA0000BCE", "protocol_version": 2 diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..29d5f734 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "ChirpStack Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.data)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.time,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fCnt,\n lora_frame_port: message.fPort,\n lora_frequency: message.txInfo.frequency,\n lora_spreading_factor:\n message?.txInfo?.modulation?.lora?.spreadingFactor ||\n message.txInfo.loRaModulationInfo.spreadingFactor,\n lora_dev_eui: (\n message?.deviceInfo?.devEui ||\n base64ToBytes(message.devEUI).reduce(\n (a, x) => a + (\"0\" + x.toString(16)).slice(-2),\n \"\",\n )\n ).toUpperCase(),\n };\n const gwi = message.rxInfo\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr || x.loRaSNR }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..1f9359b9 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..e524f89c --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload.json @@ -0,0 +1,55 @@ +{ + "adr": true, + "confirmed": false, + "data": "AgMOAANkoHmxDGA=", + "deduplicationId": "ede7dfc1-a1f5-418c-b009-dc57ebcf0895", + "devAddr": "017d1f70", + "deviceInfo": { + "applicationId": "32dafeb5-0865-4edd-bf1c-3f8d9b07af52", + "applicationName": "MoDus-Sain", + "devEui": "70b3d57ba0003bb8", + "deviceClassEnabled": "CLASS_A", + "deviceName": "PM-15288", + "deviceProfileId": "7ec3de3f-7b87-49ae-8f32-330249122a50", + "deviceProfileName": "dev-prof-Decentlab", + "tags": { + "lts": "true" + }, + "tenantId": "663cc43f-0bf2-48d4-afc6-ff27ba969f10", + "tenantName": "Friloranet" + }, + "dr": 5, + "fCnt": 20, + "fPort": 1, + "rxInfo": [ + { + "context": "AAAAAAAAAAAA/gADaoXb7A==", + "crcStatus": "CRC_OK", + "gatewayId": "fcc23dfffe2e72ed", + "gwTime": "2024-11-12T12:51:50+00:00", + "location": { + "latitude": 46.80179269386121, + "longitude": 7.1381521224975595 + }, + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "nsTime": "2024-11-12T12:51:50.819853185+00:00", + "rssi": -106, + "snr": 3.0, + "uplinkId": 3257702911 + } + ], + "time": "2024-11-10T12:51:50+00:00", + "txInfo": { + "frequency": 868300000, + "modulation": { + "lora": { + "bandwidth": 125000, + "codeRate": "CR_4_5", + "spreadingFactor": 7 + } + } + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_01.json new file mode 100644 index 00000000..f4b6b8b6 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_01.json @@ -0,0 +1,55 @@ +{ + "adr": true, + "confirmed": false, + "data": "AgMOAAIMYA==", + "deduplicationId": "ede7dfc1-a1f5-418c-b009-dc57ebcf0895", + "devAddr": "017d1f70", + "deviceInfo": { + "applicationId": "32dafeb5-0865-4edd-bf1c-3f8d9b07af52", + "applicationName": "MoDus-Sain", + "devEui": "70b3d57ba0003bb8", + "deviceClassEnabled": "CLASS_A", + "deviceName": "PM-15288", + "deviceProfileId": "7ec3de3f-7b87-49ae-8f32-330249122a50", + "deviceProfileName": "dev-prof-Decentlab", + "tags": { + "lts": "true" + }, + "tenantId": "663cc43f-0bf2-48d4-afc6-ff27ba969f10", + "tenantName": "Friloranet" + }, + "dr": 5, + "fCnt": 20, + "fPort": 1, + "rxInfo": [ + { + "context": "AAAAAAAAAAAA/gADaoXb7A==", + "crcStatus": "CRC_OK", + "gatewayId": "fcc23dfffe2e72ed", + "gwTime": "2024-11-12T12:51:50+00:00", + "location": { + "latitude": 46.80179269386121, + "longitude": 7.1381521224975595 + }, + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "nsTime": "2024-11-12T12:51:50.819853185+00:00", + "rssi": -106, + "snr": 3.0, + "uplinkId": 3257702911 + } + ], + "time": "2024-11-10T12:51:50+00:00", + "txInfo": { + "frequency": 868300000, + "modulation": { + "lora": { + "bandwidth": 125000, + "codeRate": "CR_4_5", + "spreadingFactor": 7 + } + } + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_02.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_02.json new file mode 100644 index 00000000..b02a35ee --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_02.json @@ -0,0 +1,46 @@ +{ + "applicationID": "123", + "applicationName": "temperature-sensor", + "deviceName": "garden-sensor", + "devEUI": "AgICAgICAgI=", + "rxInfo": [ + { + "gatewayID": "AwMDAwMDAwM=", + "time": "2019-11-08T13:59:25.048445Z", + "timeSinceGPSEpoch": null, + "rssi": -48, + "loRaSNR": 9, + "channel": 5, + "rfChain": 0, + "board": 0, + "antenna": 0, + "location": { + "latitude": 52.3740364, + "longitude": 4.9144401, + "altitude": 10.5 + }, + "fineTimestampType": "NONE", + "context": "9u/uvA==", + "uplinkID": "jhMh8Gq6RAOChSKbi83RHQ==" + } + ], + "txInfo": { + "frequency": 868100000, + "modulation": "LORA", + "loRaModulationInfo": { + "bandwidth": 125, + "spreadingFactor": 11, + "codeRate": "4/5", + "polarizationInversion": false + } + }, + "adr": true, + "dr": 1, + "fCnt": 10, + "fPort": 5, + "data": "AgMOAANkoHmxDGA=", + "objectJSON": "{\"temperatureSensor\":25,\"humiditySensor\":32}", + "tags": { + "key": "value" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_03.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_03.json new file mode 100644 index 00000000..75524c99 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/payload_03.json @@ -0,0 +1,46 @@ +{ + "applicationID": "123", + "applicationName": "temperature-sensor", + "deviceName": "garden-sensor", + "devEUI": "AgICAgICAgI=", + "rxInfo": [ + { + "gatewayID": "AwMDAwMDAwM=", + "time": "2019-11-08T13:59:25.048445Z", + "timeSinceGPSEpoch": null, + "rssi": -48, + "loRaSNR": 9, + "channel": 5, + "rfChain": 0, + "board": 0, + "antenna": 0, + "location": { + "latitude": 52.3740364, + "longitude": 4.9144401, + "altitude": 10.5 + }, + "fineTimestampType": "NONE", + "context": "9u/uvA==", + "uplinkID": "jhMh8Gq6RAOChSKbi83RHQ==" + } + ], + "txInfo": { + "frequency": 868100000, + "modulation": "LORA", + "loRaModulationInfo": { + "bandwidth": 125, + "spreadingFactor": 11, + "codeRate": "4/5", + "polarizationInversion": false + } + }, + "adr": true, + "dr": 1, + "fCnt": 10, + "fPort": 5, + "data": "AgMOAAIMYA==", + "objectJSON": "{\"temperatureSensor\":25,\"humiditySensor\":32}", + "tags": { + "key": "value" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result.json new file mode 100644 index 00000000..becc32c5 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0003BB8", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": "2024-11-10T12:51:50+00:00", + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 20, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 7, + "lora_rssi": -106, + "lora_snr": 3 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_01.json new file mode 100644 index 00000000..ef238e28 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0003BB8", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": "2024-11-10T12:51:50+00:00", + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 20, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 7, + "lora_rssi": -106, + "lora_snr": 3 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_02.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_02.json new file mode 100644 index 00000000..5289a4a3 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_02.json @@ -0,0 +1,23 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "0202020202020202", + "protocol_version": 2 + }, + "telemetry": [ + { + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 10, + "lora_frame_port": 5, + "lora_frequency": 868100000, + "lora_spreading_factor": 11, + "lora_rssi": -48, + "lora_snr": 9 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_03.json b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_03.json new file mode 100644 index 00000000..ada74806 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ChirpStack/uplink/result_03.json @@ -0,0 +1,21 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "0202020202020202", + "protocol_version": 2 + }, + "telemetry": [ + { + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 10, + "lora_frame_port": 5, + "lora_frequency": 868100000, + "lora_spreading_factor": 11, + "lora_rssi": -48, + "lora_snr": 9 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/converter.json new file mode 100644 index 00000000..9207d027 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "LORIOT Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.data),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: message.ts,\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.fcnt,\n lora_frame_port: message.port,\n lora_frequency: message.freq,\n lora_snr: message.snr,\n lora_rssi: message.rssi,\n lora_dev_eui: message.EUI,\n lora_spreading_factor: parseInt(message.dr.replace(/.*SF(\\d+).*/, '$1')),\n };\n if (message.cmd === 'gw') {\n const gwi = message.gws\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n result.rssi = gwi.rssi;\n result.snr = gwi.snr;\n }\n return result;\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..5c24f9c2 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "LORIOT integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload.json new file mode 100644 index 00000000..db896a43 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload.json @@ -0,0 +1,13 @@ +{ + "cmd": "rx", + "EUI": "0004A30B001ADCB5", + "ts": 1501064048945, + "fcnt": 121, + "port": 1, + "freq": 868300000, + "rssi": -119, + "snr": -15.8, + "dr": "SF11 BW125 4/5", + "ack": false, + "data": "02030e000364a079b10c60" +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_01.json new file mode 100644 index 00000000..5a049244 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_01.json @@ -0,0 +1,13 @@ +{ + "cmd": "rx", + "EUI": "0004A30B001ADCB5", + "ts": 1501064048945, + "fcnt": 121, + "port": 1, + "freq": 868300000, + "rssi": -119, + "snr": -15.8, + "dr": "SF11 BW125 4/5", + "ack": false, + "data": "02030e00020c60" +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..dbd5395a --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "02030e000364a079b10c60" +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result.json new file mode 100644 index 00000000..fd184c33 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "0004A30B001ADCB5", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1501064048945, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 121, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_snr": -15.8, + "lora_rssi": -119, + "lora_spreading_factor": 11 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_01.json new file mode 100644 index 00000000..7d9dabcc --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "0004A30B001ADCB5", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1501064048945, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 121, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_snr": -15.8, + "lora_rssi": -119, + "lora_spreading_factor": 11 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..562a1cc0 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/converter.json new file mode 100644 index 00000000..a2e25858 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "ThingPark Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/metadata.json new file mode 100644 index 00000000..d024aecf --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ThingPark integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload.json new file mode 100644 index 00000000..5adfd4bc --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload.json @@ -0,0 +1,75 @@ +{ + "DevEUI_uplink": { + "Time": "2024-11-28T21:08:22.138+00:00", + "DevEUI": "70B3D57BA000156B", + "FPort": 1, + "FCntUp": 26, + "LostUplinksAS": 0, + "ADRbit": 1, + "MType": 2, + "FCntDn": 2, + "payload_hex": "02030e000364a079b10c60", + "mic_hex": "e7214986", + "Lrcid": "00000211", + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547, + "SpFact": 9, + "SubBand": "G0", + "Channel": "LC1", + "Lrrid": "100019D4", + "Late": 0, + "LrrLAT": 32.516357, + "LrrLON": -106.824348, + "Lrrs": { + "Lrr": [ + { + "Lrrid": "100019D4", + "Chain": 0, + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547 + } + ] + }, + "DevLrrCnt": 1, + "CustomerID": "100045194", + "CustomerData": { + "loc": { + "lat": "32.592782", + "lon": "-106.927742" + }, + "alr": { + "pro": "DL/TBRG", + "ver": "1" + }, + "tags": ["EnvironSens", "RainGauge", "CDRRC"], + "doms": [], + "name": "RainGauge 5483_Test" + }, + "BaseStationData": { + "doms": [], + "name": "iStation US #6_CDRRC_Summerford" + }, + "ModelCfg": "0", + "DriverCfg": { + "mod": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + }, + "app": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + } + }, + "InstantPER": 0.0, + "MeanPER": 0.037037, + "DevAddr": "00FDA112", + "TxPower": 18.0, + "NbTrans": 2, + "Frequency": 902.5, + "DynamicClass": "A" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload_01.json new file mode 100644 index 00000000..7d0a43d9 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/payload_01.json @@ -0,0 +1,75 @@ +{ + "DevEUI_uplink": { + "Time": "2024-11-28T21:08:22.138+00:00", + "DevEUI": "70B3D57BA000156B", + "FPort": 1, + "FCntUp": 26, + "LostUplinksAS": 0, + "ADRbit": 1, + "MType": 2, + "FCntDn": 2, + "payload_hex": "02030e00020c60", + "mic_hex": "e7214986", + "Lrcid": "00000211", + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547, + "SpFact": 9, + "SubBand": "G0", + "Channel": "LC1", + "Lrrid": "100019D4", + "Late": 0, + "LrrLAT": 32.516357, + "LrrLON": -106.824348, + "Lrrs": { + "Lrr": [ + { + "Lrrid": "100019D4", + "Chain": 0, + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547 + } + ] + }, + "DevLrrCnt": 1, + "CustomerID": "100045194", + "CustomerData": { + "loc": { + "lat": "32.592782", + "lon": "-106.927742" + }, + "alr": { + "pro": "DL/TBRG", + "ver": "1" + }, + "tags": ["EnvironSens", "RainGauge", "CDRRC"], + "doms": [], + "name": "RainGauge 5483_Test" + }, + "BaseStationData": { + "doms": [], + "name": "iStation US #6_CDRRC_Summerford" + }, + "ModelCfg": "0", + "DriverCfg": { + "mod": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + }, + "app": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + } + }, + "InstantPER": 0.0, + "MeanPER": 0.037037, + "DevAddr": "00FDA112", + "TxPower": 18.0, + "NbTrans": 2, + "Frequency": 902.5, + "DynamicClass": "A" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result.json new file mode 100644 index 00000000..872843f3 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA000156B", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1732828102138, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 26, + "lora_frame_port": 1, + "lora_frequency": 902500000, + "lora_spreading_factor": 9, + "lora_rssi": -114, + "lora_snr": 4.75 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result_01.json new file mode 100644 index 00000000..bcdf7769 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingPark/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA000156B", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1732828102138, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 26, + "lora_frame_port": 1, + "lora_frequency": 902500000, + "lora_spreading_factor": 9, + "lora_rssi": -114, + "lora_snr": 4.75 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/converter.json new file mode 100644 index 00000000..534bf7f8 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "ThingParkEnterprise Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(message.DevEUI_uplink.payload_hex),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.DevEUI_uplink.Time),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n return {\n lora_frame_counter: message.DevEUI_uplink.FCntUp,\n lora_frame_port: message.DevEUI_uplink.FPort,\n lora_frequency: message.DevEUI_uplink?.Frequency * 1000000,\n lora_spreading_factor: message.DevEUI_uplink.SpFact,\n lora_dev_eui: message.DevEUI_uplink.DevEUI,\n lora_rssi: message.DevEUI_uplink.LrrRSSI,\n lora_snr: message.DevEUI_uplink.LrrSNR,\n };\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/metadata.json new file mode 100644 index 00000000..47c01690 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ThingParkEnterprise integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload.json new file mode 100644 index 00000000..5adfd4bc --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload.json @@ -0,0 +1,75 @@ +{ + "DevEUI_uplink": { + "Time": "2024-11-28T21:08:22.138+00:00", + "DevEUI": "70B3D57BA000156B", + "FPort": 1, + "FCntUp": 26, + "LostUplinksAS": 0, + "ADRbit": 1, + "MType": 2, + "FCntDn": 2, + "payload_hex": "02030e000364a079b10c60", + "mic_hex": "e7214986", + "Lrcid": "00000211", + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547, + "SpFact": 9, + "SubBand": "G0", + "Channel": "LC1", + "Lrrid": "100019D4", + "Late": 0, + "LrrLAT": 32.516357, + "LrrLON": -106.824348, + "Lrrs": { + "Lrr": [ + { + "Lrrid": "100019D4", + "Chain": 0, + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547 + } + ] + }, + "DevLrrCnt": 1, + "CustomerID": "100045194", + "CustomerData": { + "loc": { + "lat": "32.592782", + "lon": "-106.927742" + }, + "alr": { + "pro": "DL/TBRG", + "ver": "1" + }, + "tags": ["EnvironSens", "RainGauge", "CDRRC"], + "doms": [], + "name": "RainGauge 5483_Test" + }, + "BaseStationData": { + "doms": [], + "name": "iStation US #6_CDRRC_Summerford" + }, + "ModelCfg": "0", + "DriverCfg": { + "mod": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + }, + "app": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + } + }, + "InstantPER": 0.0, + "MeanPER": 0.037037, + "DevAddr": "00FDA112", + "TxPower": 18.0, + "NbTrans": 2, + "Frequency": 902.5, + "DynamicClass": "A" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload_01.json new file mode 100644 index 00000000..7d0a43d9 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/payload_01.json @@ -0,0 +1,75 @@ +{ + "DevEUI_uplink": { + "Time": "2024-11-28T21:08:22.138+00:00", + "DevEUI": "70B3D57BA000156B", + "FPort": 1, + "FCntUp": 26, + "LostUplinksAS": 0, + "ADRbit": 1, + "MType": 2, + "FCntDn": 2, + "payload_hex": "02030e00020c60", + "mic_hex": "e7214986", + "Lrcid": "00000211", + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547, + "SpFact": 9, + "SubBand": "G0", + "Channel": "LC1", + "Lrrid": "100019D4", + "Late": 0, + "LrrLAT": 32.516357, + "LrrLON": -106.824348, + "Lrrs": { + "Lrr": [ + { + "Lrrid": "100019D4", + "Chain": 0, + "LrrRSSI": -114.0, + "LrrSNR": 4.75, + "LrrESP": -115.2547 + } + ] + }, + "DevLrrCnt": 1, + "CustomerID": "100045194", + "CustomerData": { + "loc": { + "lat": "32.592782", + "lon": "-106.927742" + }, + "alr": { + "pro": "DL/TBRG", + "ver": "1" + }, + "tags": ["EnvironSens", "RainGauge", "CDRRC"], + "doms": [], + "name": "RainGauge 5483_Test" + }, + "BaseStationData": { + "doms": [], + "name": "iStation US #6_CDRRC_Summerford" + }, + "ModelCfg": "0", + "DriverCfg": { + "mod": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + }, + "app": { + "pId": "dl", + "mId": "dl-tbrg", + "ver": "1" + } + }, + "InstantPER": 0.0, + "MeanPER": 0.037037, + "DevAddr": "00FDA112", + "TxPower": 18.0, + "NbTrans": 2, + "Frequency": 902.5, + "DynamicClass": "A" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result.json new file mode 100644 index 00000000..872843f3 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA000156B", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1732828102138, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 26, + "lora_frame_port": 1, + "lora_frequency": 902500000, + "lora_spreading_factor": 9, + "lora_rssi": -114, + "lora_snr": 4.75 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result_01.json new file mode 100644 index 00000000..bcdf7769 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingParkEnterprise/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA000156B", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1732828102138, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 26, + "lora_frame_port": 1, + "lora_frequency": 902500000, + "lora_spreading_factor": 9, + "lora_rssi": -114, + "lora_snr": 4.75 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..443fb80d --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "ThingsStackCommunity Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..11a4de83 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ThingsStackCommunity integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..54f8c83f --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,60 @@ +{ + "end_device_ids": { + "device_id": "03022", + "application_ids": { + "application_id": "test-decentlab" + }, + "dev_eui": "70B3D57BA0000BCE", + "join_eui": "70B3D57ED00006B2", + "dev_addr": "27000020" + }, + "correlation_ids": [ + "as:up:01E0CY8V864TP36Q130RSQQJBY", + "gs:conn:01E0B4GJHDRFKNEQ9GG3ZDG1JX", + "gs:uplink:01E0CY8V1FFJ0WCKXTKT0CRPDY", + "ns:uplink:01E0CY8V1JGN0R5YZTBH48YXH3", + "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01E0CY8V1JHJA8PE0P98VSDE6K" + ], + "received_at": "2020-02-06T09:46:05.447941836Z", + "uplink_message": { + "session_key_id": "AXAWYbtxgUllLtJWdZrW0Q==", + "f_port": 1, + "f_cnt": 101, + "frm_payload": "AgMOAANkoHmxDGA=", + "rx_metadata": [ + { + "gateway_ids": { + "gateway_id": "eui-b827ebfffe9f2da9", + "eui": "B827EBFFFE9F2DA9" + }, + "time": "2020-02-06T09:46:05Z", + "timestamp": 436812492, + "rssi": -16, + "channel_rssi": -16, + "snr": 8.5, + "uplink_token": "CiIKIAoUZXVpLWI4MjdlYmZmZmU5ZjJkYTkSCLgn6//+ny2pEMz1pNAB", + "location": { + "latitude": 52.71863806169478, + "longitude": -4.052632749080659, + "altitude": 5, + "source": "SOURCE_REGISTRY" + }, + "channel_index": 6 + } + ], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 11 + } + }, + "data_rate_index": 1, + "coding_rate": "4/5", + "frequency": "867700000", + "timestamp": 436812492, + "time": "2020-02-06T09:46:05Z" + }, + "received_at": "2020-02-06T09:46:05.234172599Z" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload_01.json new file mode 100644 index 00000000..09bf7a63 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/payload_01.json @@ -0,0 +1,60 @@ +{ + "end_device_ids": { + "device_id": "03022", + "application_ids": { + "application_id": "test-decentlab" + }, + "dev_eui": "70B3D57BA0000BCE", + "join_eui": "70B3D57ED00006B2", + "dev_addr": "27000020" + }, + "correlation_ids": [ + "as:up:01E0CY8V864TP36Q130RSQQJBY", + "gs:conn:01E0B4GJHDRFKNEQ9GG3ZDG1JX", + "gs:uplink:01E0CY8V1FFJ0WCKXTKT0CRPDY", + "ns:uplink:01E0CY8V1JGN0R5YZTBH48YXH3", + "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01E0CY8V1JHJA8PE0P98VSDE6K" + ], + "received_at": "2020-02-06T09:46:05.447941836Z", + "uplink_message": { + "session_key_id": "AXAWYbtxgUllLtJWdZrW0Q==", + "f_port": 1, + "f_cnt": 101, + "frm_payload": "AgMOAAIMYA==", + "rx_metadata": [ + { + "gateway_ids": { + "gateway_id": "eui-b827ebfffe9f2da9", + "eui": "B827EBFFFE9F2DA9" + }, + "time": "2020-02-06T09:46:05Z", + "timestamp": 436812492, + "rssi": -16, + "channel_rssi": -16, + "snr": 8.5, + "uplink_token": "CiIKIAoUZXVpLWI4MjdlYmZmZmU5ZjJkYTkSCLgn6//+ny2pEMz1pNAB", + "location": { + "latitude": 52.71863806169478, + "longitude": -4.052632749080659, + "altitude": 5, + "source": "SOURCE_REGISTRY" + }, + "channel_index": 6 + } + ], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 11 + } + }, + "data_rate_index": 1, + "coding_rate": "4/5", + "frequency": "867700000", + "timestamp": 436812492, + "time": "2020-02-06T09:46:05Z" + }, + "received_at": "2020-02-06T09:46:05.234172599Z" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..925b1f23 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0000BCE", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1580982365447, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 101, + "lora_frame_port": 1, + "lora_frequency": 867700000, + "lora_spreading_factor": 11, + "lora_rssi": -16, + "lora_snr": 8.5 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result_01.json new file mode 100644 index 00000000..7f2fb8ca --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackCommunity/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0000BCE", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1580982365447, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 101, + "lora_frame_port": 1, + "lora_frequency": 867700000, + "lora_spreading_factor": 11, + "lora_rssi": -16, + "lora_snr": 8.5 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/converter.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..0bcd4110 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,19 @@ +{ + "name": "ThingsStackIndustries Uplink Decoder for Decentlab DL-SHT35-002", + "type": "UPLINK", + "debugMode": true, + "edgeTemplate": false, + "configuration": { + "scriptLang": "JS", + "updateOnlyKeys": [ + "lora_frame_counter", + "lora_frame_port", + "lora_frequency", + "lora_spreading_factor", + "lora_rssi", + "lora_dev_eui", + "lora_snr" + ], + "decoder": "// WARNING: This script is designed to work with a remote JS executor. Compatibility with a local JS evaluator is not guaranteed and may require rewriting.\n\nconst message = decodeToJson(payload);\nconst values = Object.assign(\n decodeDevicePayload(base64ToBytes(message.uplink_message.frm_payload)),\n extractNetworkTelemetry(message)\n);\n\nreturn {\n deviceName: String(values.device_id),\n deviceType: 'DL-SHT35-002',\n attributes: {\n lora_dev_eui: values.lora_dev_eui,\n protocol_version: values.protocol_version,\n },\n telemetry: [{\n ts: parseDateToTimestamp(message.received_at),\n values: keepTelemetry(values)\n }]\n};\n\nfunction decodeDevicePayload(payload) {\n /* https://www.decentlab.com/products/air-temperature-and-humidity-sensor-with-radiation-shield-for-lorawan */\n const decentlab_decoder = {\n PROTOCOL_VERSION: 2,\n SENSORS: [\n {length: 2,\n values: [{name: 'air_temperature',\n displayName: 'Air temperature',\n convert: function (x) { return 175 * x[0] / 65535 - 45; },\n unit: '°C'},\n {name: 'air_humidity',\n displayName: 'Air humidity',\n convert: function (x) { return 100 * x[1] / 65535; },\n unit: '%'}]},\n {length: 1,\n values: [{name: 'battery_voltage',\n displayName: 'Battery voltage',\n convert: function (x) { return x[0] / 1000; },\n unit: 'V'}]}\n ],\n read_int: function (bytes, pos) {\n return (bytes[pos] << 8) + bytes[pos + 1];\n },\n decode: function (msg) {\n var bytes = msg;\n var i, j;\n if (typeof msg === 'string') {\n bytes = [];\n for (i = 0; i < msg.length; i += 2) {\n bytes.push(parseInt(msg.substring(i, i + 2), 16));\n }\n }\n var version = bytes[0];\n if (version != this.PROTOCOL_VERSION) {\n return {error: \"protocol version \" + version + \" doesn't match v2\"};\n }\n var deviceId = this.read_int(bytes, 1);\n var flags = this.read_int(bytes, 3);\n var result = {'protocol_version': version, 'device_id': deviceId};\n // decode payload\n var pos = 5;\n for (i = 0; i < this.SENSORS.length; i++, flags >>= 1) {\n if ((flags & 1) !== 1)\n continue;\n var sensor = this.SENSORS[i];\n var x = [];\n // convert data to 16-bit integer array\n for (j = 0; j < sensor.length; j++) {\n x.push(this.read_int(bytes, pos));\n pos += 2;\n }\n // decode sensor values\n for (j = 0; j < sensor.values.length; j++) {\n var value = sensor.values[j];\n if ('convert' in value) {\n result[value.name] = {displayName: value.displayName,\n value: value.convert.bind(this)(x)};\n if ('unit' in value)\n result[value.name]['unit'] = value.unit;\n }\n }\n }\n return result;\n }\n };\n\n const decoded = decentlab_decoder.decode(payload);\n const result = {};\n for (var k in decoded) {\n if (typeof decoded[k] === \"object\") {\n result[k] = decoded[k].value;\n } else if (typeof decoded[k] === \"number\") {\n result[k] = decoded[k];\n }\n }\n return result;\n}\n\nfunction extractNetworkTelemetry(payload) {\n const result = {\n lora_frame_counter: message.uplink_message.f_cnt,\n lora_frame_port: message.uplink_message.f_port,\n lora_frequency: parseInt(message.uplink_message.settings.frequency),\n lora_dev_eui: message.end_device_ids.dev_eui,\n lora_spreading_factor:\n message.uplink_message.settings.data_rate.lora.spreading_factor,\n };\n const gwi = message.uplink_message.rx_metadata\n .map((x) => ({ lora_rssi: x.rssi, lora_snr: x.snr }))\n .reduce((a, x) => (x.lora_rssi > a.lora_rssi ? x : a), {\n lora_rssi: -Number.MAX_VALUE,\n });\n return Object.assign(result, gwi);\n}\n\n// Helper functions\n\nfunction decodeToJson(payload) {\n const s = String.fromCharCode.apply(String, payload);\n return JSON.parse(s);\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n\n return timestamp;\n}\n\nfunction base64ToBytes(base64String) {\n if (typeof base64String === 'string') {\n return Uint8Array.from(atob(base64String), c => c.charCodeAt(0));\n }\n return [];\n}\n\nfunction keepTelemetry(telemetry) {\n const unwanted = ['protocol_version', 'device_id', 'lora_dev_eui'];\n return Object.fromEntries(\n Object.entries(telemetry).filter(\n (x) => !unwanted.includes(x[0])\n )\n );\n}" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..e304ca99 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ThingsStackIndustries integration", + "includeGatewayInfo": false +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..54f8c83f --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,60 @@ +{ + "end_device_ids": { + "device_id": "03022", + "application_ids": { + "application_id": "test-decentlab" + }, + "dev_eui": "70B3D57BA0000BCE", + "join_eui": "70B3D57ED00006B2", + "dev_addr": "27000020" + }, + "correlation_ids": [ + "as:up:01E0CY8V864TP36Q130RSQQJBY", + "gs:conn:01E0B4GJHDRFKNEQ9GG3ZDG1JX", + "gs:uplink:01E0CY8V1FFJ0WCKXTKT0CRPDY", + "ns:uplink:01E0CY8V1JGN0R5YZTBH48YXH3", + "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01E0CY8V1JHJA8PE0P98VSDE6K" + ], + "received_at": "2020-02-06T09:46:05.447941836Z", + "uplink_message": { + "session_key_id": "AXAWYbtxgUllLtJWdZrW0Q==", + "f_port": 1, + "f_cnt": 101, + "frm_payload": "AgMOAANkoHmxDGA=", + "rx_metadata": [ + { + "gateway_ids": { + "gateway_id": "eui-b827ebfffe9f2da9", + "eui": "B827EBFFFE9F2DA9" + }, + "time": "2020-02-06T09:46:05Z", + "timestamp": 436812492, + "rssi": -16, + "channel_rssi": -16, + "snr": 8.5, + "uplink_token": "CiIKIAoUZXVpLWI4MjdlYmZmZmU5ZjJkYTkSCLgn6//+ny2pEMz1pNAB", + "location": { + "latitude": 52.71863806169478, + "longitude": -4.052632749080659, + "altitude": 5, + "source": "SOURCE_REGISTRY" + }, + "channel_index": 6 + } + ], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 11 + } + }, + "data_rate_index": 1, + "coding_rate": "4/5", + "frequency": "867700000", + "timestamp": 436812492, + "time": "2020-02-06T09:46:05Z" + }, + "received_at": "2020-02-06T09:46:05.234172599Z" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload_01.json new file mode 100644 index 00000000..09bf7a63 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/payload_01.json @@ -0,0 +1,60 @@ +{ + "end_device_ids": { + "device_id": "03022", + "application_ids": { + "application_id": "test-decentlab" + }, + "dev_eui": "70B3D57BA0000BCE", + "join_eui": "70B3D57ED00006B2", + "dev_addr": "27000020" + }, + "correlation_ids": [ + "as:up:01E0CY8V864TP36Q130RSQQJBY", + "gs:conn:01E0B4GJHDRFKNEQ9GG3ZDG1JX", + "gs:uplink:01E0CY8V1FFJ0WCKXTKT0CRPDY", + "ns:uplink:01E0CY8V1JGN0R5YZTBH48YXH3", + "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01E0CY8V1JHJA8PE0P98VSDE6K" + ], + "received_at": "2020-02-06T09:46:05.447941836Z", + "uplink_message": { + "session_key_id": "AXAWYbtxgUllLtJWdZrW0Q==", + "f_port": 1, + "f_cnt": 101, + "frm_payload": "AgMOAAIMYA==", + "rx_metadata": [ + { + "gateway_ids": { + "gateway_id": "eui-b827ebfffe9f2da9", + "eui": "B827EBFFFE9F2DA9" + }, + "time": "2020-02-06T09:46:05Z", + "timestamp": 436812492, + "rssi": -16, + "channel_rssi": -16, + "snr": 8.5, + "uplink_token": "CiIKIAoUZXVpLWI4MjdlYmZmZmU5ZjJkYTkSCLgn6//+ny2pEMz1pNAB", + "location": { + "latitude": 52.71863806169478, + "longitude": -4.052632749080659, + "altitude": 5, + "source": "SOURCE_REGISTRY" + }, + "channel_index": 6 + } + ], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 11 + } + }, + "data_rate_index": 1, + "coding_rate": "4/5", + "frequency": "867700000", + "timestamp": 436812492, + "time": "2020-02-06T09:46:05Z" + }, + "received_at": "2020-02-06T09:46:05.234172599Z" + } +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..925b1f23 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0000BCE", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1580982365447, + "values": { + "air_temperature": 23.787670710307466, + "air_humidity": 47.536430914778364, + "battery_voltage": 3.168, + "lora_frame_counter": 101, + "lora_frame_port": 1, + "lora_frequency": 867700000, + "lora_spreading_factor": 11, + "lora_rssi": -16, + "lora_snr": 8.5 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result_01.json b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result_01.json new file mode 100644 index 00000000..7f2fb8ca --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/ThingsStackIndustries/uplink/result_01.json @@ -0,0 +1,22 @@ +{ + "deviceName": "782", + "deviceType": "DL-SHT35-002", + "attributes": { + "lora_dev_eui": "70B3D57BA0000BCE", + "protocol_version": 2 + }, + "telemetry": [ + { + "ts": 1580982365447, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 101, + "lora_frame_port": 1, + "lora_frequency": 867700000, + "lora_spreading_factor": 11, + "lora_rssi": -16, + "lora_snr": 8.5 + } + } + ] +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/info.json b/VENDORS/Decentlab/DL-SHT35-002/info.json new file mode 100644 index 00000000..609ed0b2 --- /dev/null +++ b/VENDORS/Decentlab/DL-SHT35-002/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://www.decentlab.com/products/air-temperature-and-humidity-sensor-for-lorawan", + "label": "DL-SHT35-002: Air temperature and humidity sensor", + "description": "The Decentlab DL-SHT35-002 is equipped with a temperature and humidity sensors for environmental monitoring. Suitable for cold chain, storage, building automation, smart agriculture applications." +} diff --git a/VENDORS/Decentlab/DL-SHT35-002/photo.png b/VENDORS/Decentlab/DL-SHT35-002/photo.png new file mode 100644 index 00000000..47e5f1eb Binary files /dev/null and b/VENDORS/Decentlab/DL-SHT35-002/photo.png differ diff --git a/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..f0836c8b --- /dev/null +++ b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "020b50000309018a8c09438a9809278a920b3c8aa50c9c8a8c11e08aa500000000000000000b3b" +} diff --git a/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..619737db --- /dev/null +++ b/VENDORS/Decentlab/DL-SMTP/LORIOT/uplink/result_2.json @@ -0,0 +1,34 @@ +{ + "deviceName": "2896", + "deviceType": "DL-SMTP", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "soil_moisture_at_depth_0": -0.39, + "soil_temperature_at_depth_0": 27, + "soil_moisture_at_depth_1": -0.258, + "soil_temperature_at_depth_1": 27.12, + "soil_moisture_at_depth_2": -0.314, + "soil_temperature_at_depth_2": 27.06, + "soil_moisture_at_depth_3": 0.752, + "soil_temperature_at_depth_3": 27.25, + "soil_moisture_at_depth_4": 1.456, + "soil_temperature_at_depth_4": 27, + "soil_moisture_at_depth_5": 4.152, + "soil_temperature_at_depth_5": 27.25, + "soil_moisture_at_depth_6": -5, + "soil_temperature_at_depth_6": -327.68, + "soil_moisture_at_depth_7": -5, + "soil_temperature_at_depth_7": -327.68, + "battery_voltage": 2.875, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..5df82e32 --- /dev/null +++ b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0202f800020c54" +} diff --git a/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..1292ae6f --- /dev/null +++ b/VENDORS/Decentlab/DL-TBRG/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "760", + "deviceType": "DL-TBRG", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TP/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..455744d2 --- /dev/null +++ b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "023e3e00038abc8a928aa08a848ab38a898ac38aad8ab78a928aa1000000000000000000000afc" +} diff --git a/VENDORS/Decentlab/DL-TP/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..5c6f1e18 --- /dev/null +++ b/VENDORS/Decentlab/DL-TP/LORIOT/uplink/result_2.json @@ -0,0 +1,34 @@ +{ + "deviceName": "15934", + "deviceType": "DL-TP", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature_at_level_0": 27.48, + "temperature_at_level_1": 27.06, + "temperature_at_level_2": 27.2, + "temperature_at_level_3": 26.92, + "temperature_at_level_4": 27.39, + "temperature_at_level_5": 26.97, + "temperature_at_level_6": 27.55, + "temperature_at_level_7": 27.33, + "temperature_at_level_8": 27.43, + "temperature_at_level_9": 27.06, + "temperature_at_level_10": 27.21, + "temperature_at_level_11": -327.68, + "temperature_at_level_12": -327.68, + "temperature_at_level_13": -327.68, + "temperature_at_level_14": -327.68, + "temperature_at_level_15": -327.68, + "battery_voltage": 2.812, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/payload_1.json b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..83719e76 --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0210d50003463f810b0c79" +} diff --git a/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/result_1.json b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..bd1dc349 --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS11/LORIOT/uplink/result_1.json @@ -0,0 +1,21 @@ +{ + "deviceName": "4309", + "deviceType": "DL-TRS11", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "dielectric_permittivity": 1.0259018697121183, + "volumetric_water_content": 0.0019605699999999393, + "soil_temperature": 26.7, + "battery_voltage": 3.193, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..ea76deee --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0210d3000346be813d00000c80" +} diff --git a/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..b5f28413 --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS12/LORIOT/uplink/result_2.json @@ -0,0 +1,22 @@ +{ + "deviceName": "4307", + "deviceType": "DL-TRS12", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "dielectric_permittivity": 1.1831248966814998, + "volumetric_water_content": 0.006886900000000029, + "soil_temperature": 31.7, + "electrical_conductivity": 0, + "battery_voltage": 3.2, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..11124286 --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0201920003007580a10c25" +} diff --git a/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..db814c91 --- /dev/null +++ b/VENDORS/Decentlab/DL-TRS21/LORIOT/uplink/result_2.json @@ -0,0 +1,20 @@ +{ + "deviceName": "402", + "deviceType": "DL-TRS21", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "water_potential": -11.7, + "soil_temperature": 16.1, + "battery_voltage": 3.109, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-TRS21/info.json b/VENDORS/Decentlab/DL-TRS21/info.json index 84300074..ad9e9fc1 100644 --- a/VENDORS/Decentlab/DL-TRS21/info.json +++ b/VENDORS/Decentlab/DL-TRS21/info.json @@ -1,5 +1,5 @@ { "url": "https://www.decentlab.com/products/soil-water-potential-and-temperature-sensor-for-lorawan", - "label": "DL-TRS12: Soil water potential and temperature sensor", + "label": "DL-TRS21: Soil water potential and temperature sensor", "description": "The Decentlab DL-TRS21 is a soil water potential and temperature sensor for LoRaWAN®. Suitable for applications such as irrigation control, smart agriculture, parks, and golf courses." } \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..fdc4edeb --- /dev/null +++ b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "021a1000040c60" +} diff --git a/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3f42e961 --- /dev/null +++ b/VENDORS/Decentlab/DL-WRM/LORIOT/uplink/result_2.json @@ -0,0 +1,18 @@ +{ + "deviceName": "6672", + "deviceType": "DL-WRM", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.168, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/payload_2.json b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4b89cdb2 --- /dev/null +++ b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0211110003409a00860c54" +} diff --git a/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/result_2.json b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..6dede2dd --- /dev/null +++ b/VENDORS/Decentlab/DL-ZN1/LORIOT/uplink/result_2.json @@ -0,0 +1,19 @@ +{ + "deviceName": "4369", + "deviceType": "DL-ZN1", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "dendrometer_position": 976.9296646118164, + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/payload_1.json b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..9563f60b --- /dev/null +++ b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0211110003409a00863039003e0c54" +} diff --git a/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/result_1.json b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..0113f60a --- /dev/null +++ b/VENDORS/Decentlab/DL-ZN2/LORIOT/uplink/result_1.json @@ -0,0 +1,20 @@ +{ + "deviceName": "4369", + "deviceType": "DL-ZN2", + "attributes": { + "lora_dev_eui": "0102030405060708", + "protocol_version": 2 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "dendrometer_a_position": 976.9296646118164, + "dendrometer_b_position": 11259.996891021729, + "battery_voltage": 3.156, + "lora_frame_counter": 3, + "lora_frame_port": 1, + "lora_frequency": 868300000, + "lora_spreading_factor": 12 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Decentlab/logo.svg b/VENDORS/Decentlab/logo.svg index e70014a4..aea4f022 100644 --- a/VENDORS/Decentlab/logo.svg +++ b/VENDORS/Decentlab/logo.svg @@ -1,5 +1,5 @@ - - + + diff --git a/VENDORS/Elsys/ERS2/LORIOT/uplink/converter.json b/VENDORS/Elsys/ERS2/LORIOT/uplink/converter.json index ee7b05b9..f29d11f1 100644 --- a/VENDORS/Elsys/ERS2/LORIOT/uplink/converter.json +++ b/VENDORS/Elsys/ERS2/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Elsys ERS2 Device", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735046487936 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 \" + data.EUI;\nvar deviceType = \"ERS2\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Motion (PIR)\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 \" + data.EUI;\nvar deviceType = \"ERS2\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Motion (PIR)\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Elsys/ERS2/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..8742d0c5 --- /dev/null +++ b/VENDORS/Elsys/ERS2/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e202290400270506060308070d62" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..10fd392e --- /dev/null +++ b/VENDORS/Elsys/ERS2/LORIOT/uplink/result_1.json @@ -0,0 +1,45 @@ +[{ + "deviceName": "ERS2 0102030405060708", + "deviceType": "ERS2", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "light": 39, + "pir": 6, + "vdd": 3426 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/converter.json b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/converter.json index 2c462394..024f2cb1 100644 --- a/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/converter.json +++ b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Elsys ERS2 C02 Device", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735046693295 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 CO2 \" + data.EUI;\nvar deviceType = \"ERS2 CO2\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Pir\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x06) { // CO₂\n decoded.co2 = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 CO2 \" + data.EUI;\nvar deviceType = \"ERS2 CO2\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Pir\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x06) { // CO₂\n decoded.co2 = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..8742d0c5 --- /dev/null +++ b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e202290400270506060308070d62" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..aa38c3e6 --- /dev/null +++ b/VENDORS/Elsys/ERS2_CO2/LORIOT/uplink/result_1.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "ERS2 CO2 0102030405060708", + "deviceType": "ERS2 CO2", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "light": 39, + "pir": 6, + "co2": 776, + "vdd": 3426 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/converter.json b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/converter.json index 9dc26aa4..80acd49e 100644 --- a/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/converter.json +++ b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Elsys ERS2 C02 Lite Device", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735046825246 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 CO2 Lite \" + data.EUI;\nvar deviceType = \"ERS2 CO2 Lite\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x06) { // CO₂\n decoded.co2 = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 CO2 Lite \" + data.EUI;\nvar deviceType = \"ERS2 CO2 Lite\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x06) { // CO₂\n decoded.co2 = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..74aa5ddf --- /dev/null +++ b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e20229060308070d62" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..025cbacf --- /dev/null +++ b/VENDORS/Elsys/ERS2_CO2_Lite/LORIOT/uplink/result_1.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "ERS2 CO2 Lite 0102030405060708", + "deviceType": "ERS2 CO2 Lite", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "co2": 776, + "vdd": 3426 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/converter.json b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/converter.json index 5f810faa..3907eece 100644 --- a/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/converter.json +++ b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Elsys ERS2 Lite Device", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735046911061 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 Lite \" + data.EUI;\nvar deviceType = \"ERS2 Lite\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 Lite \" + data.EUI;\nvar deviceType = \"ERS2 Lite\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..399b7aed --- /dev/null +++ b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e20229070d62" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..12f206e3 --- /dev/null +++ b/VENDORS/Elsys/ERS2_Lite/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "ERS2 Lite 0102030405060708", + "deviceType": "ERS2 Lite", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "vdd": 3426 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..f8ae5607 --- /dev/null +++ b/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e202290400270506070d62150710" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..8d488ad7 --- /dev/null +++ b/VENDORS/Elsys/ERS2_Sound/LORIOT/uplink/result_1.json @@ -0,0 +1,47 @@ +[{ + "deviceName": "ERS2 Sound0102030405060708", + "deviceType": "ERS2 Sound", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "light": 39, + "pir": 6, + "vdd": 3426, + "soundPeak": 7, + "soundAvg": 16 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/converter.json b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/converter.json index 97a1a346..98c43a05 100644 --- a/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/converter.json +++ b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for Elsys ERS2 VOC Device", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735047102023 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 VOC \" + data.EUI;\nvar deviceType = \"ERS2 VOC\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Pir\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n } else if (type == 0x1c) { // TVOC\n decoded.tvoc = parseBytesToInt(input, i, 2, true);\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"ERS2 VOC \" + data.EUI;\nvar deviceType = \"ERS2 VOC\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length;) {\n var type = input[i++] & 0xff;\n \n if (type == 0x01) { \n // Temperature Converts to °C\n decoded.temperature = parseBytesToInt(input, i, 2, true) / 10;\n i += 2;\n } else if (type == 0x02) { // Humidity\n decoded.humidity = input[i];\n i += 1;\n } else if (type == 0x04) { // Light\n decoded.light = parseBytesToInt(input, i, 2, true);\n i += 2;\n } else if (type == 0x05) { // Pir\n decoded.pir = input[i];\n i += 1;\n } else if (type == 0x07) { // Battery Voltage (VDD)\n decoded.vdd = parseBytesToInt(input, i, 2, true); \n i += 2;\n } else if (type == 0x1c) { // TVOC\n decoded.tvoc = parseBytesToInt(input, i, 2, true);\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/payload_1.json b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..350156c4 --- /dev/null +++ b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0100e202290400270506070d621c0d62" +} \ No newline at end of file diff --git a/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/result_1.json b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..a9fc9d8a --- /dev/null +++ b/VENDORS/Elsys/ERS2_VOC/LORIOT/uplink/result_1.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "ERS2 VOC 0102030405060708", + "deviceType": "ERS2 VOC", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 22.6, + "humidity": 41, + "light": 39, + "pir": 6, + "vdd": 3426, + "tvoc": 3426 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Elsys/logo.svg b/VENDORS/Elsys/logo.svg index 0c865b13..11e2c6a8 100644 --- a/VENDORS/Elsys/logo.svg +++ b/VENDORS/Elsys/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/Milesight/AM102/HTTP/uplink/converter.json b/VENDORS/Milesight/AM102/HTTP/uplink/converter.json new file mode 100644 index 00000000..9f971faf --- /dev/null +++ b/VENDORS/Milesight/AM102/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM102", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102 \" + data.devEUI;\nvar deviceType = \"AM102\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n historyDataList.add(historyData);\n \n i += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM102/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM102/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102/HTTP/uplink/payload.json b/VENDORS/Milesight/AM102/HTTP/uplink/payload.json new file mode 100644 index 00000000..dddfb341 --- /dev/null +++ b/VENDORS/Milesight/AM102/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cYAQRobQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102/HTTP/uplink/result.json b/VENDORS/Milesight/AM102/HTTP/uplink/result.json new file mode 100644 index 00000000..f8c9c0d2 --- /dev/null +++ b/VENDORS/Milesight/AM102/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "AM102 24e1641092176759", + "deviceType": "AM102", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM102/LORIOT/uplink/converter.json index 371d29e6..90cce360 100644 --- a/VENDORS/Milesight/AM102/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM102/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM102", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207152023 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102 \" + data.EUI;\nvar deviceType = \"AM102\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n i += 7;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102 \" + data.EUI;\nvar deviceType = \"AM102\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n i += 7;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM102/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM102/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..c34d2110 --- /dev/null +++ b/VENDORS/Milesight/AM102/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175640367180104686D" +} diff --git a/VENDORS/Milesight/AM102/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM102/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..10e240d0 --- /dev/null +++ b/VENDORS/Milesight/AM102/LORIOT/uplink/result_2.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "AM102 0102030405060708", + "deviceType": "AM102", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/HTTP/uplink/converter.json b/VENDORS/Milesight/AM102L/HTTP/uplink/converter.json new file mode 100644 index 00000000..c1c0f59c --- /dev/null +++ b/VENDORS/Milesight/AM102L/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM102L", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102L \" + data.devEUI;\nvar deviceType = \"AM102L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n historyDataList.add(historyData);\n \n i += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM102L/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM102L/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/HTTP/uplink/payload.json b/VENDORS/Milesight/AM102L/HTTP/uplink/payload.json new file mode 100644 index 00000000..dddfb341 --- /dev/null +++ b/VENDORS/Milesight/AM102L/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cYAQRobQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/HTTP/uplink/result.json b/VENDORS/Milesight/AM102L/HTTP/uplink/result.json new file mode 100644 index 00000000..a98a4529 --- /dev/null +++ b/VENDORS/Milesight/AM102L/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "AM102L 24e1641092176759", + "deviceType": "AM102L", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM102L/LORIOT/uplink/converter.json index 0e084f22..4b85acf1 100644 --- a/VENDORS/Milesight/AM102L/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM102L/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM102L", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207408456 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102L \" + data.EUI;\nvar deviceType = \"AM102L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n i += 7;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM102L \" + data.EUI;\nvar deviceType = \"AM102L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n }\n };\n \n i += 7;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM102L/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM102L/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..f2493a25 --- /dev/null +++ b/VENDORS/Milesight/AM102L/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175640367180104686D" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM102L/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM102L/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..f6f029e6 --- /dev/null +++ b/VENDORS/Milesight/AM102L/LORIOT/uplink/result_2.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "AM102L 0102030405060708", + "deviceType": "AM102L", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM103/HTTP/uplink/converter.json b/VENDORS/Milesight/AM103/HTTP/uplink/converter.json new file mode 100644 index 00000000..e7ecb462 --- /dev/null +++ b/VENDORS/Milesight/AM103/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM103", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103 \" + data.devEUI;\nvar deviceType = \"AM103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n \n // HISTORY DATA \n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n historyDataList.add(historyData);\n i += 9;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM103/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM103/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103/HTTP/uplink/payload.json b/VENDORS/Milesight/AM103/HTTP/uplink/payload.json new file mode 100644 index 00000000..ecdffd17 --- /dev/null +++ b/VENDORS/Milesight/AM103/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cYAQRobQd9xQEgzngaQWXfAG6QAQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103/HTTP/uplink/result.json b/VENDORS/Milesight/AM103/HTTP/uplink/result.json new file mode 100644 index 00000000..eda2ebec --- /dev/null +++ b/VENDORS/Milesight/AM103/HTTP/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "AM103 24e1641092176759", + "deviceType": "AM103", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5, + "co2": 453 + } + }, { + "ts": 1698765432000, + "values": { + "temperature": 22.3, + "humidity": 55.0, + "co2": 400 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM103/LORIOT/uplink/converter.json index 48a16552..97747b65 100644 --- a/VENDORS/Milesight/AM103/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM103/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-103", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207546204 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103 \" + data.EUI;\nvar deviceType = \"AM103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n i += 9;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103 \" + data.EUI;\nvar deviceType = \"AM103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n i += 9;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM103/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM103/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..70b4f245 --- /dev/null +++ b/VENDORS/Milesight/AM103/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175640367180104686d077dc50120ce781a4165df006e9001" +} diff --git a/VENDORS/Milesight/AM103/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM103/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..517aec53 --- /dev/null +++ b/VENDORS/Milesight/AM103/LORIOT/uplink/result_2.json @@ -0,0 +1,51 @@ +[{ + "deviceName": "AM103 0102030405060708", + "deviceType": "AM103", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5, + "co2": 453 + } + }, { + "ts": 1698765432000, + "values": { + "temperature": 22.3, + "humidity": 55.0, + "co2": 400 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM103L/HTTP/uplink/converter.json b/VENDORS/Milesight/AM103L/HTTP/uplink/converter.json new file mode 100644 index 00000000..a864c5f8 --- /dev/null +++ b/VENDORS/Milesight/AM103L/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM103L", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103L \" + data.devEUI;\nvar deviceType = \"AM103L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n \n // HISTORY DATA \n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n historyDataList.add(historyData);\n i += 9;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103L/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM103L/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM103L/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103L/HTTP/uplink/payload.json b/VENDORS/Milesight/AM103L/HTTP/uplink/payload.json new file mode 100644 index 00000000..ecdffd17 --- /dev/null +++ b/VENDORS/Milesight/AM103L/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cYAQRobQd9xQEgzngaQWXfAG6QAQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103L/HTTP/uplink/result.json b/VENDORS/Milesight/AM103L/HTTP/uplink/result.json new file mode 100644 index 00000000..5a0f60a9 --- /dev/null +++ b/VENDORS/Milesight/AM103L/HTTP/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "AM103L 24e1641092176759", + "deviceType": "AM103L", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5, + "co2": 453 + } + }, { + "ts": 1698765432000, + "values": { + "temperature": 22.3, + "humidity": 55.0, + "co2": 400 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM103L/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM103L/LORIOT/uplink/converter.json index 77e3f11b..916a1344 100644 --- a/VENDORS/Milesight/AM103L/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM103L/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-103L", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207669353 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103L \" + data.EUI;\nvar deviceType = \"AM103L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n i += 9;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM103L \" + data.EUI;\nvar deviceType = \"AM103L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 1, false) / 2,\n co2: parseBytesToInt(input, i + 7, 2, false),\n }\n };\n \n i += 9;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM103L/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM103L/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..d1ffb584 --- /dev/null +++ b/VENDORS/Milesight/AM103L/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175640367180104686d077dc501" +} diff --git a/VENDORS/Milesight/AM103L/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM103L/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..1b598cac --- /dev/null +++ b/VENDORS/Milesight/AM103L/LORIOT/uplink/result_2.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "AM103L 0102030405060708", + "deviceType": "AM103L", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.0, + "humidity": 54.5, + "co2": 453 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM104/HTTP/uplink/converter.json b/VENDORS/Milesight/AM104/HTTP/uplink/converter.json new file mode 100644 index 00000000..82c6db13 --- /dev/null +++ b/VENDORS/Milesight/AM104/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM104", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM104 \" + data.devEUI;\nvar deviceType = \"AM104\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM104/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM104/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM104/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM104/HTTP/uplink/payload.json b/VENDORS/Milesight/AM104/HTTP/uplink/payload.json new file mode 100644 index 00000000..6b65f9fd --- /dev/null +++ b/VENDORS/Milesight/AM104/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVcA2c0AQRoZQVqSQAGZRwAeQAUAA==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM104/HTTP/uplink/result.json b/VENDORS/Milesight/AM104/HTTP/uplink/result.json new file mode 100644 index 00000000..c4d9ddd2 --- /dev/null +++ b/VENDORS/Milesight/AM104/HTTP/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "AM104 24e1641092176759", + "deviceType": "AM104", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 92, + "temperature": 30.8, + "humidity": 50.5, + "activity": 73, + "illumination": 28, + "infrared_and_visible": 121, + "infrared": 20 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM104/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM104/LORIOT/uplink/converter.json index 06bc9d1b..73cb172f 100644 --- a/VENDORS/Milesight/AM104/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM104/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { - "name": "Uplink data converter for Loriot integration AM-103", + "name": "Uplink data converter for Loriot integration AM-104", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207754530 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM104 \" + data.EUI;\nvar deviceType = \"AM104\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM104 \" + data.EUI;\nvar deviceType = \"AM104\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n \n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM104/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/AM104/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..740f1ea6 --- /dev/null +++ b/VENDORS/Milesight/AM104/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01755C03673401046865056A490006651C0079001400" +} diff --git a/VENDORS/Milesight/AM104/LORIOT/uplink/result_1.json b/VENDORS/Milesight/AM104/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..225c921a --- /dev/null +++ b/VENDORS/Milesight/AM104/LORIOT/uplink/result_1.json @@ -0,0 +1,47 @@ +[{ + "deviceName": "AM104 0102030405060708", + "deviceType": "AM104", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 92, + "temperature": 30.8, + "humidity": 50.5, + "activity": 73, + "illumination": 28, + "infrared_and_visible": 121, + "infrared": 20 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM107/HTTP/uplink/converter.json b/VENDORS/Milesight/AM107/HTTP/uplink/converter.json new file mode 100644 index 00000000..a779b6d0 --- /dev/null +++ b/VENDORS/Milesight/AM107/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM107", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM107 \" + data.devEUI;\nvar deviceType = \"AM107\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n \n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM107/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM107/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM107/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM107/HTTP/uplink/payload.json b/VENDORS/Milesight/AM107/HTTP/uplink/payload.json new file mode 100644 index 00000000..e475505f --- /dev/null +++ b/VENDORS/Milesight/AM107/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVcA2c0AQRoZQVqSQAGZRwAeQAUAAd95wQIfQcACXM/Jw==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM107/HTTP/uplink/result.json b/VENDORS/Milesight/AM107/HTTP/uplink/result.json new file mode 100644 index 00000000..0e9b1315 --- /dev/null +++ b/VENDORS/Milesight/AM107/HTTP/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "AM107 24e1641092176759", + "deviceType": "AM107", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 92, + "temperature": 30.8, + "humidity": 50.5, + "activity": 73, + "illumination": 28, + "infrared_and_visible": 121, + "infrared": 20, + "co2": 1255, + "tvoc": 7, + "pressure": 1004.7 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM107/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM107/LORIOT/uplink/converter.json index 4e0df159..78fb90ed 100644 --- a/VENDORS/Milesight/AM107/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM107/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-107", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735207888088 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM107 \" + data.EUI;\nvar deviceType = \"AM107\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM107 \" + data.EUI;\nvar deviceType = \"AM107\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x6a) {\n decoded.activity = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0x65) {\n decoded.illumination = parseBytesToInt(input, i, 2, false);\n decoded.infrared_and_visible = parseBytesToInt(input, i+2, 2, false);\n decoded.infrared = parseBytesToInt(input, i+4, 2, false);\n i += 6;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM107/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/AM107/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..45990550 --- /dev/null +++ b/VENDORS/Milesight/AM107/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01755C03673401046865056A490006651C0079001400077DE704087D070009733F27" +} diff --git a/VENDORS/Milesight/AM107/LORIOT/uplink/result_1.json b/VENDORS/Milesight/AM107/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..f6a79af1 --- /dev/null +++ b/VENDORS/Milesight/AM107/LORIOT/uplink/result_1.json @@ -0,0 +1,50 @@ +[{ + "deviceName": "AM107 0102030405060708", + "deviceType": "AM107", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 92, + "temperature": 30.8, + "humidity": 50.5, + "activity": 73, + "illumination": 28, + "infrared_and_visible": 121, + "infrared": 20, + "co2": 1255, + "tvoc": 7, + "pressure": 1004.7 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM307/HTTP/uplink/converter.json b/VENDORS/Milesight/AM307/HTTP/uplink/converter.json new file mode 100644 index 00000000..490e72b9 --- /dev/null +++ b/VENDORS/Milesight/AM307/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM307", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307 \" + data.devEUI;\nvar deviceType = \"AM307\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n }\n };\n \n historyDataList.add(historyData);\n \n i += 16;\n } \n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM307/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM307/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307/HTTP/uplink/payload.json b/VENDORS/Milesight/AM307/HTTP/uplink/payload.json new file mode 100644 index 00000000..aa80cdcc --- /dev/null +++ b/VENDORS/Milesight/AM307/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA19IAAOAVUgzmU1ygwA4QBuAWQBwgAKJ5cAGQAy" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307/HTTP/uplink/result.json b/VENDORS/Milesight/AM307/HTTP/uplink/result.json new file mode 100644 index 00000000..110663eb --- /dev/null +++ b/VENDORS/Milesight/AM307/HTTP/uplink/result.json @@ -0,0 +1,37 @@ +{ + "deviceName": "AM307 24e1641092176759", + "deviceType": "AM307", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM307/LORIOT/uplink/converter.json index 267c88d9..b947c6f9 100644 --- a/VENDORS/Milesight/AM307/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM307/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-307", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735208030138 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307 \" + data.EUI;\nvar deviceType = \"AM307\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10\n }\n };\n \n i += 16;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307 \" + data.EUI;\nvar deviceType = \"AM307\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10\n }\n };\n \n historyDataList.add(historyData);\n \n i += 16;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM307/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM307/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..313d704f --- /dev/null +++ b/VENDORS/Milesight/AM307/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000d7d20000e015520ce6535ca0c00e1006e016401c2000a279700190032" +} diff --git a/VENDORS/Milesight/AM307/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM307/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..c2c8646d --- /dev/null +++ b/VENDORS/Milesight/AM307/LORIOT/uplink/result_2.json @@ -0,0 +1,60 @@ +[{ + "deviceName": "AM307 0102030405060708", + "deviceType": "AM307", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM307L/HTTP/uplink/converter.json b/VENDORS/Milesight/AM307L/HTTP/uplink/converter.json new file mode 100644 index 00000000..3b4ca37f --- /dev/null +++ b/VENDORS/Milesight/AM307L/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM307L", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307L \" + data.devEUI;\nvar deviceType = \"AM307L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n }\n };\n \n historyDataList.add(historyData);\n \n i += 16;\n } \n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307L/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM307L/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM307L/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307L/HTTP/uplink/payload.json b/VENDORS/Milesight/AM307L/HTTP/uplink/payload.json new file mode 100644 index 00000000..aa80cdcc --- /dev/null +++ b/VENDORS/Milesight/AM307L/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA19IAAOAVUgzmU1ygwA4QBuAWQBwgAKJ5cAGQAy" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307L/HTTP/uplink/result.json b/VENDORS/Milesight/AM307L/HTTP/uplink/result.json new file mode 100644 index 00000000..7d3b2eea --- /dev/null +++ b/VENDORS/Milesight/AM307L/HTTP/uplink/result.json @@ -0,0 +1,37 @@ +{ + "deviceName": "AM307L 24e1641092176759", + "deviceType": "AM307L", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM307L/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM307L/LORIOT/uplink/converter.json index 5fc200c7..9077b685 100644 --- a/VENDORS/Milesight/AM307L/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM307L/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-307L", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735208254904 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307L \" + data.EUI;\nvar deviceType = \"AM307L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10\n }\n };\n \n i += 16;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM307L \" + data.EUI;\nvar deviceType = \"AM307L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10\n }\n };\n \n i += 16;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM307L/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM307L/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..313d704f --- /dev/null +++ b/VENDORS/Milesight/AM307L/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000d7d20000e015520ce6535ca0c00e1006e016401c2000a279700190032" +} diff --git a/VENDORS/Milesight/AM307L/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM307L/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..5e5cd2d1 --- /dev/null +++ b/VENDORS/Milesight/AM307L/LORIOT/uplink/result_2.json @@ -0,0 +1,60 @@ +[{ + "deviceName": "AM307L 0102030405060708", + "deviceType": "AM307L", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/HTTP/uplink/converter.json b/VENDORS/Milesight/AM308/HTTP/uplink/converter.json new file mode 100644 index 00000000..a6b4e9e5 --- /dev/null +++ b/VENDORS/Milesight/AM308/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM308", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308 \" + data.devEUI;\nvar deviceType = \"AM308\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0B && channel_type === 0x7D) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0C && channel_type === 0x7D) {\n decoded.pm10 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n historyDataList.add(historyData);\n \n i += 20;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM308/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM308/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/HTTP/uplink/payload.json b/VENDORS/Milesight/AM308/HTTP/uplink/payload.json new file mode 100644 index 00000000..aa80cdcc --- /dev/null +++ b/VENDORS/Milesight/AM308/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA19IAAOAVUgzmU1ygwA4QBuAWQBwgAKJ5cAGQAy" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/HTTP/uplink/result.json b/VENDORS/Milesight/AM308/HTTP/uplink/result.json new file mode 100644 index 00000000..b7d5a1e8 --- /dev/null +++ b/VENDORS/Milesight/AM308/HTTP/uplink/result.json @@ -0,0 +1,41 @@ +{ + "deviceName": "AM308 24e1641092176759", + "deviceType": "AM308", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM308/LORIOT/uplink/converter.json index 53c4938e..a76ee69d 100644 --- a/VENDORS/Milesight/AM308/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM308/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-308", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735208595731 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308 \" + data.EUI;\nvar deviceType = \"AM308\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // PM2_5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n else if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n i += 20;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308 \" + data.EUI;\nvar deviceType = \"AM308\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // PM2_5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n else if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n historyDataList.add(historyData);\n \n i += 20;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM308/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM308/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..79534d6c --- /dev/null +++ b/VENDORS/Milesight/AM308/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000d7d20000e015520ce6535ca0c00e1006e016401c2000a279700190032" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM308/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..41f65138 --- /dev/null +++ b/VENDORS/Milesight/AM308/LORIOT/uplink/result_2.json @@ -0,0 +1,64 @@ +[{ + "deviceName": "AM308 0102030405060708", + "deviceType": "AM308", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM308L/HTTP/uplink/converter.json b/VENDORS/Milesight/AM308L/HTTP/uplink/converter.json new file mode 100644 index 00000000..2a49dd3a --- /dev/null +++ b/VENDORS/Milesight/AM308L/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM308L", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308L \" + data.devEUI;\nvar deviceType = \"AM308L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0B && channel_type === 0x7D) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0C && channel_type === 0x7D) {\n decoded.pm10 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n historyDataList.add(historyData);\n \n i += 20;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308L/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM308L/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM308L/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308L/HTTP/uplink/payload.json b/VENDORS/Milesight/AM308L/HTTP/uplink/payload.json new file mode 100644 index 00000000..aa80cdcc --- /dev/null +++ b/VENDORS/Milesight/AM308L/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA19IAAOAVUgzmU1ygwA4QBuAWQBwgAKJ5cAGQAy" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308L/HTTP/uplink/result.json b/VENDORS/Milesight/AM308L/HTTP/uplink/result.json new file mode 100644 index 00000000..3499c5b0 --- /dev/null +++ b/VENDORS/Milesight/AM308L/HTTP/uplink/result.json @@ -0,0 +1,41 @@ +{ + "deviceName": "AM308L 24e1641092176759", + "deviceType": "AM308L", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM308L/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM308L/LORIOT/uplink/converter.json index 4a086272..18290e35 100644 --- a/VENDORS/Milesight/AM308L/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM308L/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-308L", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735208821523 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308L \" + data.EUI;\nvar deviceType = \"AM308L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // PM2_5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n else if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n i += 20;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM308L \" + data.EUI;\nvar deviceType = \"AM308L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // PM2_5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n else if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0e && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n // HISTORY DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false)\n }\n };\n \n historyDataList.add(historyData);\n \n i += 20;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM308L/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM308L/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..313d704f --- /dev/null +++ b/VENDORS/Milesight/AM308L/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000d7d20000e015520ce6535ca0c00e1006e016401c2000a279700190032" +} diff --git a/VENDORS/Milesight/AM308L/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM308L/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..1132e127 --- /dev/null +++ b/VENDORS/Milesight/AM308L/LORIOT/uplink/result_2.json @@ -0,0 +1,64 @@ +[{ + "deviceName": "AM308L 0102030405060708", + "deviceType": "AM308L", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM319/HTTP/uplink/converter.json b/VENDORS/Milesight/AM319/HTTP/uplink/converter.json new file mode 100644 index 00000000..7f1a17e0 --- /dev/null +++ b/VENDORS/Milesight/AM319/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM319", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319 \" + data.devEUI;\nvar deviceType = \"AM319\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n i += 22;\n }*/\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM319/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM319/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319/HTTP/uplink/payload.json b/VENDORS/Milesight/AM319/HTTP/uplink/payload.json new file mode 100644 index 00000000..7e6e0ec1 --- /dev/null +++ b/VENDORS/Milesight/AM319/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA4BVSDOZTXKDADhAG4BZAHCAAonlwAZADIEAA==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319/HTTP/uplink/result.json b/VENDORS/Milesight/AM319/HTTP/uplink/result.json new file mode 100644 index 00000000..919cfb66 --- /dev/null +++ b/VENDORS/Milesight/AM319/HTTP/uplink/result.json @@ -0,0 +1,43 @@ +{ + "deviceName": "AM319 24e1641092176759", + "deviceType": "AM319", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "hcho": 0.04, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800, + "hcho": 0.04 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM319/LORIOT/uplink/converter.json index 181ef04d..e00c8a11 100644 --- a/VENDORS/Milesight/AM319/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM319/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-319", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735209025134 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319 \" + data.EUI;\nvar deviceType = \"AM319\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n i += 22;\n }*/\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319 \" + data.EUI;\nvar deviceType = \"AM319\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n \n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n \n i += 22;\n }*/\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM319/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM319/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..197d64b0 --- /dev/null +++ b/VENDORS/Milesight/AM319/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000e015520ce6535ca0c00e1006e016401c2000a2797001900320400" +} diff --git a/VENDORS/Milesight/AM319/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM319/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..abfe0c09 --- /dev/null +++ b/VENDORS/Milesight/AM319/LORIOT/uplink/result_2.json @@ -0,0 +1,66 @@ +[{ + "deviceName": "AM319 0102030405060708", + "deviceType": "AM319", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "hcho": 0.04, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800, + "hcho": 0.04 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AM319L/HTTP/uplink/converter.json b/VENDORS/Milesight/AM319L/HTTP/uplink/converter.json new file mode 100644 index 00000000..6f4eb723 --- /dev/null +++ b/VENDORS/Milesight/AM319L/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AM319L", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319L \" + data.devEUI;\nvar deviceType = \"AM319L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" :\n \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xCB) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7D) {\n decoded.co2 = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7D) {\n decoded.tvoc = parseBytesToInt(input, i, 2,\n false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2,\n false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n i += 22;\n }*/\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319L/HTTP/uplink/metadata.json b/VENDORS/Milesight/AM319L/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AM319L/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319L/HTTP/uplink/payload.json b/VENDORS/Milesight/AM319L/HTTP/uplink/payload.json new file mode 100644 index 00000000..7e6e0ec1 --- /dev/null +++ b/VENDORS/Milesight/AM319L/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA4BVSDOZTXKDADhAG4BZAHCAAonlwAZADIEAA==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319L/HTTP/uplink/result.json b/VENDORS/Milesight/AM319L/HTTP/uplink/result.json new file mode 100644 index 00000000..8a223814 --- /dev/null +++ b/VENDORS/Milesight/AM319L/HTTP/uplink/result.json @@ -0,0 +1,43 @@ +{ + "deviceName": "AM319L 24e1641092176759", + "deviceType": "AM319L", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "hcho": 0.04, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800, + "hcho": 0.04 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AM319L/LORIOT/uplink/converter.json b/VENDORS/Milesight/AM319L/LORIOT/uplink/converter.json index 08228bb0..d2348d18 100644 --- a/VENDORS/Milesight/AM319L/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AM319L/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AM-319L", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735209232367 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319L \" + data.EUI;\nvar deviceType = \"AM319L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n i += 22;\n }*/\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AM319L \" + data.EUI;\nvar deviceType = \"AM319L\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // HUMIDITY\n if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // PIR\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.pir = input[i] === 1 ? \"trigger\" : \"idle\";\n i += 1;\n }\n // LIGHT\n if (channel_id === 0x06 && channel_type === 0xcb) {\n decoded.light_level = input[i];\n i += 1;\n }\n // CO2\n if (channel_id === 0x07 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // TVOC\n if (channel_id === 0x08 && channel_type === 0x7d) {\n decoded.tvoc = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n if (channel_id === 0x09 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // HCHO\n if (channel_id === 0x0a && channel_type === 0x7d) {\n decoded.hcho = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // PM2.5\n if (channel_id === 0x0b && channel_type === 0x7d) {\n decoded.pm2_5 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PM10\n if (channel_id === 0x0c && channel_type === 0x7d) {\n decoded.pm10 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // O3\n if (channel_id === 0x0d && channel_type === 0x7d) {\n decoded.o3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // BEEP\n if (channel_id === 0x0E && channel_type === 0x01) {\n decoded.beep = input[i] === 1 ? \"yes\" : \"no\";\n i += 1;\n }\n \n // HISTORY DATA (CH2O)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n hcho: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n \n i += 22;\n }\n \n // HISTORY DATA (O3)\n /*if (channel_id === 0x20 && channel_type === 0xCE) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n temperature: parseBytesToInt(input, i + 4, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 6, 2, false) / 2,\n pir: input[i + 8] === 1 ? \"trigger\" : \"idle\",\n light_level: input[i + 9],\n co2: parseBytesToInt(input, i + 10, 2, false),\n tvoc: parseBytesToInt(input, i + 12, 2, false),\n pressure: parseBytesToInt(input, i + 14, 2, false) / 10,\n pm2_5: parseBytesToInt(input, i + 16, 2, false),\n pm10: parseBytesToInt(input, i + 18, 2, false),\n o3: parseBytesToInt(input, i + 20, 2, false) / 100\n }\n };\n \n historyDataList.add(historyData);\n \n i += 22;\n }*/\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AM319L/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AM319L/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..197d64b0 --- /dev/null +++ b/VENDORS/Milesight/AM319L/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000e015520ce6535ca0c00e1006e016401c2000a2797001900320400" +} diff --git a/VENDORS/Milesight/AM319L/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AM319L/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3833a17c --- /dev/null +++ b/VENDORS/Milesight/AM319L/LORIOT/uplink/result_2.json @@ -0,0 +1,66 @@ +[{ + "deviceName": "AM319L 0102030405060708", + "deviceType": "AM319L", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 85, + "temperature": 23.8, + "humidity": 62.0, + "pir": "trigger", + "light_level": 2, + "co2": 936, + "tvoc": 37, + "pressure": 1008.6, + "hcho": 0.04, + "pm2_5": 32, + "pm10": 48, + "beep": "no" + } + }, { + "ts": 214578533000, + "values": { + "temperature": 5760.0, + "humidity": 14080.0, + "trigger": "idle", + "light_level": 100, + "co2": 49665, + "tvoc": 2560, + "pressure": 3869.5, + "pm2_5": 6400, + "pm10": 12800, + "hcho": 0.04 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/AT101/HTTP/uplink/converter.json b/VENDORS/Milesight/AT101/HTTP/uplink/converter.json new file mode 100644 index 00000000..edcee7d4 --- /dev/null +++ b/VENDORS/Milesight/AT101/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for AT101", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AT101 \" + data.devEUI;\nvar deviceType = \"AT101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i,\n 2, false) / 10;\n i += 2;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // Location\n if ((channel_id === 0x04 || channel_id == 0x84) && channel_type === 0x88) {\n decoded.latitude = parseBytesToInt(input, i, 4, false) / 1000000;\n decoded.longitude = parseBytesToInt(input, i+4, 4, false) / 1000000;\n var status = input[i+8];\n decoded.motion_status = getMotionStatus(status & 0x0f);\n decoded.geofence_status = getGeofenceStatus((status >> 4) & 0x0f);\n i +=9;\n }\n // Position\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // Wi-Fi SCAN RESULT\n if (channel_id === 0x06 && channel_type === 0xd9) {\n var wifiObject = {};\n wifiObject.group = input[i];\n var macBytes = java.util.Arrays.copyOfRange(input, i + 1, i + 7);\n wifiObject.mac = readMAC(macBytes);\n wifiObject.rssi = input[i+7];\n wifiObject.motion_status = getMotionStatus(input[i + 8] & 0x0f);\n i += 9;\n \n decoded.wifi_scan_result = \"finish\";\n if (wifiObject.mac === \"ff:ff:ff:ff:ff:ff\") {\n decoded.wifi_scan_result = \"timeout\";\n continue;\n }\n decoded.motion_status = wifiObject.motion_status;\n \n wifi.add(wifiObject);\n }\n //Tamper Status\n if (channel_id === 0x07 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"install\" : \"uninstall\";\n i +=1;\n }\n // TEMPERATURE WITH ABNORMAL\n if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // HISTORICAL DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n historyData = {\n ts : parseBytesToInt(input, i, 4, false),\n values : {\n longitude : parseBytesToInt(input, i + 4, 4, false) / 1000000,\n latitude : parseBytesToInt(input, i + 8, 4, false) / 1000000\n }\n };\n \n historyDataList.add(historyData);\n i += 12;\n } \n }\n \n decoded.wifi = wifi.size > 0 ? wifi : null;\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getMotionStatus(status) {\n if(status == 0) {\n return \"unknown\";\n }\n else if (status == 1) {\n return \"start moving\";\n }\n else if (status == 2) {\n return \"moving\";\n }\n else {\n return \"stop moving\";\n }\n}\n\nfunction getGeofenceStatus(status) {\n if(status == 0) {\n return \"inside\";\n }\n else if (status == 1) {\n return \"outside\";\n }\n else if (status == 2) {\n return \"unset\";\n }\n else {\n return \"unknown\";\n }\n}\n\nfunction readMAC(bytes) {\n var temp = [];\n for (b : bytes) {\n temp.add(String.format(\"%02x\", b & 0xFF));\n }\n \n return String.join(\":\", temp);\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/AT101/HTTP/uplink/metadata.json b/VENDORS/Milesight/AT101/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/AT101/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AT101/HTTP/uplink/payload.json b/VENDORS/Milesight/AT101/HTTP/uplink/payload.json new file mode 100644 index 00000000..22b32fe3 --- /dev/null +++ b/VENDORS/Milesight/AT101/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cSAQUAAQbZACThJPW3l7MABtkAJOEk/wAEyAAG2QAk4STzGajBAAbZAAZQwg6qjcUABtkAJOEk9yHEuQA=" +} \ No newline at end of file diff --git a/VENDORS/Milesight/AT101/HTTP/uplink/result.json b/VENDORS/Milesight/AT101/HTTP/uplink/result.json new file mode 100644 index 00000000..b2aa22d3 --- /dev/null +++ b/VENDORS/Milesight/AT101/HTTP/uplink/result.json @@ -0,0 +1,48 @@ +{ + "deviceName": "AT101 24e1641092176759", + "deviceType": "AT101", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 27.4, + "position": "tilt", + "wifi_scan_result": "finish", + "motion_status": "unknown", + "wifi": [{ + "group": 0, + "mac": "24:e1:24:f5:b7:97", + "rssi": -77, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:ff:00:04", + "rssi": -56, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:f3:19:a8", + "rssi": -63, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "06:50:c2:0e:aa:8d", + "rssi": -59, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:f7:21:c4", + "rssi": -71, + "motion_status": "unknown" + }] + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/AT101/LORIOT/uplink/converter.json b/VENDORS/Milesight/AT101/LORIOT/uplink/converter.json index b48a8e47..4235e59b 100644 --- a/VENDORS/Milesight/AT101/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/AT101/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration AT101", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735209505483 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AT101 \" + data.EUI;\nvar deviceType = \"AT101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyData = {};\n var wifi = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // Location\n if ((channel_id === 0x04 || channel_id == 0x84) && channel_type === 0x88) {\n decoded.latitude = parseBytesToInt(input, i, 4, false) / 1000000;\n decoded.longitude = parseBytesToInt(input, i+4, 4, false) / 1000000;\n var status = input[i+8];\n decoded.motion_status = getMotionStatus(status & 0x0f);\n decoded.geofence_status = getGeofenceStatus((status >> 4) & 0x0f);\n i +=9;\n }\n // Position\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // Wi-Fi SCAN RESULT\n if (channel_id === 0x06 && channel_type === 0xd9) {\n var wifiObject = {};\n wifiObject.group = input[i];\n wifiObject.mac = readMAC(input.slice(i + 1, i + 7));\n wifiObject.rssi = input[i+7];\n wifiObject.motion_status = getMotionStatus(input[i + 8] & 0x0f);\n i += 9;\n\n decoded.wifi_scan_result = \"finish\";\n if (wifiObject.mac === \"ff:ff:ff:ff:ff:ff\") {\n decoded.wifi_scan_result = \"timeout\";\n continue;\n }\n decoded.motion_status = wifiObject.motion_status;\n\n wifi.add(wifiObject);\n }\n //Tamper Status\n if (channel_id === 0x07 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"install\" : \"uninstall\";\n i +=1;\n }\n // TEMPERATURE WITH ABNORMAL\n if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // HISTORICAL DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n historyData = {\n ts : parseBytesToInt(input, i, 4, false),\n values : {\n longitude : parseBytesToInt(input, i + 4, 4, false) / 1000000,\n latitude : parseBytesToInt(input, i + 8, 4, false) / 1000000\n }\n };\n i += 12;\n } \n }\n \n decoded.wifi = wifi.size > 0 ? wifi : null;\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyData];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction getMotionStatus(status) {\n if(status == 0) {\n return \"unknown\";\n }\n else if (status == 1) {\n return \"start moving\";\n }\n else if (status == 2) {\n return \"moving\";\n }\n else {\n return \"stop moving\";\n }\n}\n\nfunction getGeofenceStatus(status) {\n if(status == 0) {\n return \"inside\";\n }\n else if (status == 1) {\n return \"outside\";\n }\n else if (status == 2) {\n return \"unset\";\n }\n else {\n return \"unknown\";\n }\n}\n\nfunction readMAC(bytes) {\n var temp = [];\n for (b : bytes) {\n temp.add(String.format(\"%02x\", b & 0xFF));\n }\n return String.join(\":\", temp);\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"AT101 \" + data.EUI;\nvar deviceType = \"AT101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n var wifi = [];\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPERATURE\n if (channel_id === 0x03 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n i += 2;\n }\n // Location\n if ((channel_id === 0x04 || channel_id == 0x84) && channel_type === 0x88) {\n decoded.latitude = parseBytesToInt(input, i, 4, false) / 1000000;\n decoded.longitude = parseBytesToInt(input, i+4, 4, false) / 1000000;\n var status = input[i+8];\n decoded.motion_status = getMotionStatus(status & 0x0f);\n decoded.geofence_status = getGeofenceStatus((status >> 4) & 0x0f);\n i +=9;\n }\n // Position\n if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // Wi-Fi SCAN RESULT\n if (channel_id === 0x06 && channel_type === 0xd9) {\n var wifiObject = {};\n wifiObject.group = input[i];\n wifiObject.mac = readMAC(input.slice(i + 1, i + 7));\n wifiObject.rssi = input[i+7];\n wifiObject.motion_status = getMotionStatus(input[i + 8] & 0x0f);\n i += 9;\n\n decoded.wifi_scan_result = \"finish\";\n if (wifiObject.mac === \"ff:ff:ff:ff:ff:ff\") {\n decoded.wifi_scan_result = \"timeout\";\n continue;\n }\n decoded.motion_status = wifiObject.motion_status;\n\n wifi.add(wifiObject);\n }\n //Tamper Status\n if (channel_id === 0x07 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"install\" : \"uninstall\";\n i +=1;\n }\n // TEMPERATURE WITH ABNORMAL\n if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // HISTORICAL DATA\n if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts : parseBytesToInt(input, i, 4, false),\n values : {\n longitude : parseBytesToInt(input, i + 4, 4, false) / 1000000,\n latitude : parseBytesToInt(input, i + 8, 4, false) / 1000000\n }\n };\n \n historyDataList.add(historyData);\n \n i += 12;\n } \n }\n \n decoded.wifi = wifi.size > 0 ? wifi : null;\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getMotionStatus(status) {\n if(status == 0) {\n return \"unknown\";\n }\n else if (status == 1) {\n return \"start moving\";\n }\n else if (status == 2) {\n return \"moving\";\n }\n else {\n return \"stop moving\";\n }\n}\n\nfunction getGeofenceStatus(status) {\n if(status == 0) {\n return \"inside\";\n }\n else if (status == 1) {\n return \"outside\";\n }\n else if (status == 2) {\n return \"unset\";\n }\n else {\n return \"unknown\";\n }\n}\n\nfunction readMAC(bytes) {\n var temp = [];\n for (b : bytes) {\n temp.add(String.format(\"%02x\", b & 0xFF));\n }\n return String.join(\":\", temp);\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/AT101/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/AT101/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..72fe9064 --- /dev/null +++ b/VENDORS/Milesight/AT101/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0175640367120105000106d90024e124f5b797b30006d90024e124ff0004c80006d90024e124f319a8c10006d9000650c20eaa8dc50006d90024e124f721c4b900" +} diff --git a/VENDORS/Milesight/AT101/LORIOT/uplink/result_2.json b/VENDORS/Milesight/AT101/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3fd16ece --- /dev/null +++ b/VENDORS/Milesight/AT101/LORIOT/uplink/result_2.json @@ -0,0 +1,71 @@ +[{ + "deviceName": "AT101 0102030405060708", + "deviceType": "AT101", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 27.4, + "position": "tilt", + "wifi_scan_result": "finish", + "motion_status": "unknown", + "wifi": [{ + "group": 0, + "mac": "24:e1:24:f5:b7:97", + "rssi": -77, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:ff:00:04", + "rssi": -56, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:f3:19:a8", + "rssi": -63, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "06:50:c2:0e:aa:8d", + "rssi": -59, + "motion_status": "unknown" + }, { + "group": 0, + "mac": "24:e1:24:f7:21:c4", + "rssi": -71, + "motion_status": "unknown" + }] + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/CT101-CT103/ChirpStack/uplink/converter.json b/VENDORS/Milesight/CT101-CT103/ChirpStack/uplink/converter.json index e925431a..e2e79533 100644 --- a/VENDORS/Milesight/CT101-CT103/ChirpStack/uplink/converter.json +++ b/VENDORS/Milesight/CT101-CT103/ChirpStack/uplink/converter.json @@ -1,5 +1,5 @@ { - "name": "ChirpStack uplink converter for Milesight AT101/103", + "name": "ChirpStack uplink converter for Milesight CT101/103", "type": "UPLINK", "debugMode": true, "configuration": { diff --git a/VENDORS/Milesight/CT101-CT103/HTTP/uplink/converter.json b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/converter.json new file mode 100644 index 00000000..029fcce4 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/converter.json @@ -0,0 +1,31 @@ +{ + "name": "HTTP Uplink integration for CT101/103", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"CT101/103 \" + data.devEUI;\nvar deviceType = \"CT101/103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // POWER STATE\n if (channel_id === 0xff && channel_type === 0x0b) {\n decoded.power = \"on\";\n i += 1;\n }\n // IPSO VERSION\n if (channel_id === 0xff && channel_type === 0x01) {\n output.attributes.ipso_version = readProtocolVersion(input[i]);\n i += 1;\n }\n // PRODUCT SERIAL NUMBER\n if (channel_id === 0xff && channel_type === 0x16) {\n output.attributes.sn = bytesToHex(java.util.Arrays.copyOfRange(input, i, i + 8));\n i += 8;\n }\n // HARDWARE VERSION\n if (channel_id === 0xff && channel_type === 0x09) {\n output.attributes.hardware_version = readHardwareVersion(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // FIRMWARE VERSION\n if (channel_id === 0xff && channel_type === 0x0a) {\n output.attributes.firmware_version = readFirmwareVersion(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // TOTAL CURRENT\n if (channel_id === 0x03 && channel_type === 0x97) {\n decoded.total_current = parseBytesToInt(input, i, 4, false) / 100;\n i += 4;\n }\n // CURRENT\n if (channel_id === 0x04 && channel_type === 0x98) {\n var value = parseBytesToInt(input, i, 2, false);\n if (value === 0xffff) {\n decoded.alarm = \"read failed\";\n } else {\n decoded.current = value / 100;\n }\n i += 2;\n }\n // TEMPERATURE\n if (channel_id === 0x09 && channel_type === 0x67) {\n var temperature_value = parseBytesToInt(input, i, 2, false);\n if (temperature_value === 0xfffd) {\n decoded.temperature_exception = \"over range alarm\";\n } else if (temperature_value === 0xffff) {\n decoded.temperature_exception = \"read failed\";\n } else {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n }\n i += 2;\n }\n // CURRENT ALARM\n if (channel_id === 0x84 && channel_type === 0x98) {\n decoded.current_max = parseBytesToInt(input, i, 2, false) / 100;\n decoded.current_min = parseBytesToInt(input, i + 2, 2, false) / 100;\n decoded.current = parseBytesToInt(input, i + 4, 2, false) / 100;\n decoded.alarm = readCurrentAlarm(input[i + 6]);\n i += 7;\n }\n // TEMPERATURE ALARM\n if (channel_id === 0x89 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readProtocolVersion(bytes) {\n var major = (bytes & 0xf0) >> 4;\n var minor = bytes & 0x0f;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readHardwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = (bytes[1] & 0xff) >> 4;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readFirmwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = bytes[1] & 0xff;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readCurrentAlarm(type) {\n var alarm = [];\n if ((type >> 0 & 0x01) != 0) {\n alarm.add(\"threshold alarm\");\n }\n if ((type >> 1 & 0x01) != 0) {\n alarm.add(\"threshold alarm release\");\n }\n if ((type >> 2 & 0x01) != 0) {\n alarm.add(\"over range alarm\");\n }\n if ((type >> 3 & 0x01) != 0) {\n alarm.add(\"over range alarm release\");\n }\n \n return alarm;\n}\n\nfunction readTemperatureAlarm(type) {\n var alarms = [];\n \n if ((type & 0x01) != 0) {\n alarms.add(\"Threshold alarm\");\n }\n if ((type & 0x02) != 0) {\n alarms.add(\"Threshold alarm dismiss\");\n }\n\n return alarms;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "hardware_version", + "firmware_version" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/CT101-CT103/HTTP/uplink/metadata.json b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/CT101-CT103/HTTP/uplink/payload.json b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/payload.json new file mode 100644 index 00000000..b6100ab3 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "/wv//wEB/xZnRtOIAlgAAP8JAQD/CgEB/w8A" +} \ No newline at end of file diff --git a/VENDORS/Milesight/CT101-CT103/HTTP/uplink/result.json b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/result.json new file mode 100644 index 00000000..0792a6a2 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/HTTP/uplink/result.json @@ -0,0 +1,22 @@ +{ + "deviceName": "CT101/103 24e1641092176759", + "deviceType": "CT101/103", + "attributes": { + "ipso_version": "v0.1", + "sn": "6746D38802580000", + "hardware_version": "v1.0", + "firmware_version": "v1.1", + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "power": "on" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/converter.json b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/converter.json index c9ce64f0..4262820c 100644 --- a/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration СТ101/СТ103", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735209748497 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"CT101/CT103 \" + data.EUI;\nvar deviceType = \"CT101/CT103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // POWER STATE\n if (channel_id === 0xff && channel_type === 0x0b) {\n decoded.power = \"on\";\n i += 1;\n }\n // IPSO VERSION\n if (channel_id === 0xff && channel_type === 0x01) {\n decoded.ipso_version = readProtocolVersion(input[i]);\n i += 1;\n }\n // PRODUCT SERIAL NUMBER\n if (channel_id === 0xff && channel_type === 0x16) {\n decoded.sn = bytesToHex(input.slice(i, i + 8));\n i += 8;\n }\n // HARDWARE VERSION\n if (channel_id === 0xff && channel_type === 0x09) {\n decoded.hardware_version = readHardwareVersion(input.slice(i, i + 2));\n i += 2;\n }\n // FIRMWARE VERSION\n if (channel_id === 0xff && channel_type === 0x0a) {\n decoded.firmware_version = readFirmwareVersion(input.slice(i, i + 2));\n i += 2;\n }\n // TOTAL CURRENT\n if (channel_id === 0x03 && channel_type === 0x97) {\n decoded.total_current = parseBytesToInt(input, i, 4, false) / 100;\n i += 4;\n }\n // CURRENT\n if (channel_id === 0x04 && channel_type === 0x98) {\n var value = parseBytesToInt(input, i, 2, false);\n if (value === 0xffff) {\n decoded.alarm = \"read failed\";\n } else {\n decoded.current = value / 100;\n }\n i += 2;\n }\n // TEMPERATURE\n if (channel_id === 0x09 && channel_type === 0x67) {\n var temperature_value = parseBytesToInt(input, i, 2, false);\n if (temperature_value === 0xfffd) {\n decoded.temperature_exception = \"over range alarm\";\n } else if (temperature_value === 0xffff) {\n decoded.temperature_exception = \"read failed\";\n } else {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n }\n i += 2;\n }\n // CURRENT ALARM\n if (channel_id === 0x84 && channel_type === 0x98) {\n decoded.current_max = parseBytesToInt(input, i, 2, false) / 100;\n decoded.current_min = parseBytesToInt(input, i+2, 2, false) / 100;\n decoded.current = parseBytesToInt(input, i+4, 2, false) / 100;\n decoded.alarm = readCurrentAlarm(input[i + 6]);\n i += 7;\n }\n // TEMPERATURE ALARM\n if (channel_id === 0x89 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction readProtocolVersion(bytes) {\n var major = (bytes & 0xf0) >> 4;\n var minor = bytes & 0x0f;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readHardwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = (bytes[1] & 0xff) >> 4;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readFirmwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = bytes[1] & 0xff;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readCurrentAlarm(type) {\n var alarm = [];\n if ((type >> 0 & 0x01) != 0) {\n alarm.add(\"threshold alarm\");\n }\n if ((type >> 1 & 0x01) != 0) {\n alarm.add(\"threshold alarm release\");\n }\n if ((type >> 2 & 0x01) != 0) {\n alarm.add(\"over range alarm\");\n }\n if ((type >> 3 & 0x01) != 0) {\n alarm.add(\"over range alarm release\");\n }\n \n return alarm;\n}\n\nfunction readTemperatureAlarm(type) {\n var alarms = [];\n \n if ((type & 0x01) != 0) {\n alarms.add(\"Threshold alarm\");\n }\n if ((type & 0x02) != 0) {\n alarms.add(\"Threshold alarm dismiss\");\n }\n\n return alarms;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"CT101/CT103 \" + data.EUI;\nvar deviceType = \"CT101/CT103\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // POWER STATE\n if (channel_id === 0xff && channel_type === 0x0b) {\n decoded.power = \"on\";\n i += 1;\n }\n // IPSO VERSION\n if (channel_id === 0xff && channel_type === 0x01) {\n decoded.ipso_version = readProtocolVersion(input[i]);\n i += 1;\n }\n // PRODUCT SERIAL NUMBER\n if (channel_id === 0xff && channel_type === 0x16) {\n decoded.sn = bytesToHex(input.slice(i, i + 8));\n i += 8;\n }\n // HARDWARE VERSION\n if (channel_id === 0xff && channel_type === 0x09) {\n decoded.hardware_version = readHardwareVersion(input.slice(i, i + 2));\n i += 2;\n }\n // FIRMWARE VERSION\n if (channel_id === 0xff && channel_type === 0x0a) {\n decoded.firmware_version = readFirmwareVersion(input.slice(i, i + 2));\n i += 2;\n }\n // TOTAL CURRENT\n if (channel_id === 0x03 && channel_type === 0x97) {\n decoded.total_current = parseBytesToInt(input, i, 4, false) / 100;\n i += 4;\n }\n // CURRENT\n if (channel_id === 0x04 && channel_type === 0x98) {\n var value = parseBytesToInt(input, i, 2, false);\n if (value === 0xffff) {\n decoded.alarm = \"read failed\";\n } else {\n decoded.current = value / 100;\n }\n i += 2;\n }\n // TEMPERATURE\n if (channel_id === 0x09 && channel_type === 0x67) {\n var temperature_value = parseBytesToInt(input, i, 2, false);\n if (temperature_value === 0xfffd) {\n decoded.temperature_exception = \"over range alarm\";\n } else if (temperature_value === 0xffff) {\n decoded.temperature_exception = \"read failed\";\n } else {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n }\n i += 2;\n }\n // CURRENT ALARM\n if (channel_id === 0x84 && channel_type === 0x98) {\n decoded.current_max = parseBytesToInt(input, i, 2, false) / 100;\n decoded.current_min = parseBytesToInt(input, i+2, 2, false) / 100;\n decoded.current = parseBytesToInt(input, i+4, 2, false) / 100;\n decoded.alarm = readCurrentAlarm(input[i + 6]);\n i += 7;\n }\n // TEMPERATURE ALARM\n if (channel_id === 0x89 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readProtocolVersion(bytes) {\n var major = (bytes & 0xf0) >> 4;\n var minor = bytes & 0x0f;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readHardwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = (bytes[1] & 0xff) >> 4;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readFirmwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = bytes[1] & 0xff;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readCurrentAlarm(type) {\n var alarm = [];\n if ((type >> 0 & 0x01) != 0) {\n alarm.add(\"threshold alarm\");\n }\n if ((type >> 1 & 0x01) != 0) {\n alarm.add(\"threshold alarm release\");\n }\n if ((type >> 2 & 0x01) != 0) {\n alarm.add(\"over range alarm\");\n }\n if ((type >> 3 & 0x01) != 0) {\n alarm.add(\"over range alarm release\");\n }\n \n return alarm;\n}\n\nfunction readTemperatureAlarm(type) {\n var alarms = [];\n \n if ((type & 0x01) != 0) {\n alarms.add(\"Threshold alarm\");\n }\n if ((type & 0x02) != 0) {\n alarms.add(\"Threshold alarm dismiss\");\n }\n\n return alarms;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/payload_5.json b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/payload_5.json new file mode 100644 index 00000000..ece22ce2 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/payload_5.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "039710270000" +} diff --git a/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/result_5.json b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/result_5.json new file mode 100644 index 00000000..3c208ab8 --- /dev/null +++ b/VENDORS/Milesight/CT101-CT103/LORIOT/uplink/result_5.json @@ -0,0 +1,41 @@ +[{ + "deviceName": "CT101/CT103 0102030405060708", + "deviceType": "CT101/CT103", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "total_current": 100.0 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/DS3604/HTTP/uplink/converter.json b/VENDORS/Milesight/DS3604/HTTP/uplink/converter.json new file mode 100644 index 00000000..b02062b4 --- /dev/null +++ b/VENDORS/Milesight/DS3604/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for DS3604", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"DS3604 \" + data.devEUI;\nvar deviceType = \"DS3604\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPLATE\n else if (channel_id == 0xff && channel_type == 0x73) {\n decoded.template = input[i] + 1;\n i += 1;\n }\n // TEMPLATE BLOCK CHANNEL DATA\n if (channel_id == 0xfb && channel_type == 0x01) {\n var template_id = (input[i] >> 6) + 1;\n var block_id = input[i++] & 0x3f;\n var block_name;\n if (block_id < 10) {\n block_name = \"text_\" + (block_id + 1);\n block_length = input[i++];\n var bytes = java.util.Arrays.copyOfRange(input, i, i + block_length);\n decoded[block_name] = fromUtf8Bytes(bytes);\n i += block_length;\n } else if (block_id == 10) {\n block_name = \"qrcode\";\n block_length = input[i++];\n var bytes = java.util.Arrays.copyOfRange(input, i, i + block_length);\n decoded[block_name] = fromUtf8Bytes(bytes);\n i += block_length;\n }\n decoded.template = template_id;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction fromUtf8Bytes(bytes) {\n var resultObject = \"\";\n \n foreach (b : bytes) {\n resultObject += String.format(\"%%%02X\", b & 0xFF);\n }\n \n return decodeUrlEncodedString(resultObject);\n}\n\nfunction decodeUrlEncodedString(encoded) {\n var decoded = \"\";\n for (int i = 0; i < encoded.length(); i++) {\n char c = encoded.charAt(i);\n if (c == '%' && i + 2 < encoded.length()) {\n var hexValue = \"\" + encoded.charAt(i + 1) + encoded.charAt(i + 2);\n var decodedChar = Integer.parseInt(hexValue, 16);\n decoded += (char) decodedChar;\n i += 2;\n } else {\n decoded += c;\n }\n }\n return decoded;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/DS3604/HTTP/uplink/metadata.json b/VENDORS/Milesight/DS3604/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/DS3604/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/DS3604/HTTP/uplink/payload.json b/VENDORS/Milesight/DS3604/HTTP/uplink/payload.json new file mode 100644 index 00000000..9138f014 --- /dev/null +++ b/VENDORS/Milesight/DS3604/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVk+wEABU1pbGVz+wEKBWhlbGxv" +} \ No newline at end of file diff --git a/VENDORS/Milesight/DS3604/HTTP/uplink/result.json b/VENDORS/Milesight/DS3604/HTTP/uplink/result.json new file mode 100644 index 00000000..485ec86f --- /dev/null +++ b/VENDORS/Milesight/DS3604/HTTP/uplink/result.json @@ -0,0 +1,21 @@ +{ + "deviceName": "DS3604 24e1641092176759", + "deviceType": "DS3604", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "text_1": "Miles", + "template": 1, + "qrcode": "hello" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/DS3604/LORIOT/uplink/converter.json b/VENDORS/Milesight/DS3604/LORIOT/uplink/converter.json index b726608e..717d4287 100644 --- a/VENDORS/Milesight/DS3604/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/DS3604/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Uplink data converter for Loriot integration DS3604", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735209985818 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"DS3604 \" + data.EUI;\nvar deviceType = \"DS3604\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPLATE\n else if (channel_id == 0xff && channel_type == 0x73) {\n decoded.template = input[i] + 1;\n i += 1;\n }\n // TEMPLATE BLOCK CHANNEL DATA\n if (channel_id == 0xfb && channel_type == 0x01) {\n var template_id = (input[i] >> 6) + 1;\n var block_id = input[i++] & 0x3f;\n var block_name;\n if (block_id < 10) {\n block_name = \"text_\" + (block_id + 1);\n block_length = input[i++];\n decoded[block_name] = fromUtf8Bytes(input.slice(i, i + block_length));\n i += block_length;\n } else if (block_id == 10) {\n block_name = \"qrcode\";\n block_length = input[i++];\n decoded[block_name] = fromUtf8Bytes(input.slice(i, i + block_length));\n i += block_length;\n }\n decoded.template = template_id;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar includeGatewayInfo = [\"ts\", \"gweui\", \"rssi\"];\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": parseDateToTimestamp(gatewayInfo.ts),\n \"values\": getDataList(gatewayInfo, includeGatewayInfo)\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction fromUtf8Bytes(bytes) {\n var resultObject = \"\";\n \n foreach (b : bytes) {\n resultObject += String.format(\"%%%02X\", b & 0xFF);\n }\n \n return decodeUrlEncodedString(resultObject);\n}\n\nfunction decodeUrlEncodedString(encoded) {\n var decoded = \"\";\n for (int i = 0; i < encoded.length(); i++) {\n char c = encoded.charAt(i);\n if (c == '%' && i + 2 < encoded.length()) {\n var hexValue = \"\" + encoded.charAt(i + 1) + encoded.charAt(i + 2);\n var decodedChar = Integer.parseInt(hexValue, 16);\n decoded += (char) decodedChar;\n i += 2;\n } else {\n decoded += c;\n }\n }\n return decoded;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"DS3604 \" + data.EUI;\nvar deviceType = \"DS3604\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if(channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i]; \n i += 1;\n }\n // TEMPLATE\n else if (channel_id == 0xff && channel_type == 0x73) {\n decoded.template = input[i] + 1;\n i += 1;\n }\n // TEMPLATE BLOCK CHANNEL DATA\n if (channel_id == 0xfb && channel_type == 0x01) {\n var template_id = (input[i] >> 6) + 1;\n var block_id = input[i++] & 0x3f;\n var block_name;\n if (block_id < 10) {\n block_name = \"text_\" + (block_id + 1);\n block_length = input[i++];\n decoded[block_name] = fromUtf8Bytes(input.slice(i, i + block_length));\n i += block_length;\n } else if (block_id == 10) {\n block_name = \"qrcode\";\n block_length = input[i++];\n decoded[block_name] = fromUtf8Bytes(input.slice(i, i + block_length));\n i += block_length;\n }\n decoded.template = template_id;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar addDataToTelemetry = {};\naddDataToTelemetry.rssi = data.rssi;\naddDataToTelemetry.seqno = data.seqno;\naddDataToTelemetry.snr = data.snr;\naddDataToTelemetry.ack = data.ack;\naddDataToTelemetry.toa = data.toa;\naddDataToTelemetry.fCnt = data.fcnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var timestamp = -1;\n if (dateString != null) {\n \n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf('.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf('+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf('-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex + 3) {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0, secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex, dateString.length);\n }\n \n timestamp = new Date(dateString).getTime();\n }\n }\n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction fromUtf8Bytes(bytes) {\n var resultObject = \"\";\n \n foreach (b : bytes) {\n resultObject += String.format(\"%%%02X\", b & 0xFF);\n }\n \n return decodeUrlEncodedString(resultObject);\n}\n\nfunction decodeUrlEncodedString(encoded) {\n var decoded = \"\";\n for (int i = 0; i < encoded.length(); i++) {\n char c = encoded.charAt(i);\n if (c == '%' && i + 2 < encoded.length()) {\n var hexValue = \"\" + encoded.charAt(i + 1) + encoded.charAt(i + 2);\n var decodedChar = Integer.parseInt(hexValue, 16);\n decoded += (char) decodedChar;\n i += 2;\n } else {\n decoded += c;\n }\n }\n return decoded;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/DS3604/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/DS3604/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..8ce483f7 --- /dev/null +++ b/VENDORS/Milesight/DS3604/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017564FB0100054D696C6573FB010A0568656C6C6F" +} diff --git a/VENDORS/Milesight/DS3604/LORIOT/uplink/result_1.json b/VENDORS/Milesight/DS3604/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..c3d8ca86 --- /dev/null +++ b/VENDORS/Milesight/DS3604/LORIOT/uplink/result_1.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "DS3604 0102030405060708", + "deviceType": "DS3604", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "text_1": "Miles", + "template": 1, + "qrcode": "hello" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM300-CL/HTTP/uplink/converter.json b/VENDORS/Milesight/EM300-CL/HTTP/uplink/converter.json new file mode 100644 index 00000000..489ec9d4 --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM-300CL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM-300CL \" + data.devEUI;\nvar deviceType = \"EM-300CL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // LIQUID\n else if (channel_id === 0x03 && channel_type === 0xed) {\n decoded.liquid = readLiquidStatus(input[i]);\n i += 1;\n }\n // CALIBRATION RESULT\n else if (channel_id === 0x04 && channel_type === 0xee) {\n decoded.calibration_result = input[i] === 0 ? \"failed\" : \"success\";\n i += 1;\n }\n // LIQUID ALARM\n else if (channel_id === 0x83 && channel_type === 0xed) {\n decoded.liquid = readLiquidStatus(input[i]);\n decoded.liquid_alarm = readAlarmType(input[i + 1]);\n i += 2;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readLiquidStatus(type) {\n switch (type) {\n case 0:\n return \"uncalibrated\";\n case 1:\n return \"full\";\n case 2:\n return \"empty\";\n case 0xff:\n return \"error\";\n default:\n return \"unknown\";\n }\n}\n\nfunction readAlarmType(type) {\n switch (type) {\n case 0:\n return \"empty alarm release\";\n case 1:\n return \"empty alarm\";\n default:\n return \"unknown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM300-CL/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM300-CL/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM300-CL/HTTP/uplink/payload.json b/VENDORS/Milesight/EM300-CL/HTTP/uplink/payload.json new file mode 100644 index 00000000..fedc4e44 --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA+0A" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM300-CL/HTTP/uplink/result.json b/VENDORS/Milesight/EM300-CL/HTTP/uplink/result.json new file mode 100644 index 00000000..5651d6d8 --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/HTTP/uplink/result.json @@ -0,0 +1,19 @@ +{ + "deviceName": "EM-300CL 24e1641092176759", + "deviceType": "EM-300CL", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "liquid": "uncalibrated" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM300-CL/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM300-CL/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..913623be --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01756403ED00" +} diff --git a/VENDORS/Milesight/EM300-CL/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM300-CL/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..0b9e3aab --- /dev/null +++ b/VENDORS/Milesight/EM300-CL/LORIOT/uplink/result_1.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM-300CL", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "liquid": "uncalibrated" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/HTTP/uplink/converter.json b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/converter.json new file mode 100644 index 00000000..d1854fd5 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM310-TILT", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM310-TILT \" + data.devEUI;\nvar deviceType = \"EM310-TILT\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // ANGLE\n else if (channel_id === 0x03 && channel_type === 0xcf) {\n decoded.angle_x = parseBytesToInt(input, i, 2, false) / 100;\n decoded.angle_y = parseBytesToInt(input, i + 2, 2, false) / 100;\n decoded.angle_z = parseBytesToInt(input, i + 4, 2, false) / 100;\n decoded.threshold_x = (input[i + 6] & 0x01) === 0x01 ? \"trigger\" : \"normal\";\n decoded.threshold_y = (input[i + 6] & 0x02) === 0x02 ? \"trigger\" : \"normal\";\n decoded.threshold_z = (input[i + 6] & 0x04) === 0x04 ? \"trigger\" : \"normal\";\n i += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readLiquidStatus(type) {\n switch (type) {\n case 0:\n return \"uncalibrated\";\n case 1:\n return \"full\";\n case 2:\n return \"empty\";\n case 0xff:\n return \"error\";\n default:\n return \"unknown\";\n }\n}\n\nfunction readAlarmType(type) {\n switch (type) {\n case 0:\n return \"empty alarm release\";\n case 1:\n return \"empty alarm\";\n default:\n return \"unknown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/HTTP/uplink/payload.json b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/payload.json new file mode 100644 index 00000000..89357630 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "A88AAAAAKCMH" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/HTTP/uplink/result.json b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/result.json new file mode 100644 index 00000000..90818c23 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/HTTP/uplink/result.json @@ -0,0 +1,23 @@ +{ + "deviceName": "EM310-TILT 24e1641092176759", + "deviceType": "EM310-TILT", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "angle_x": 0.0, + "angle_y": 0.0, + "angle_z": 90.0, + "threshold_x": "trigger", + "threshold_y": "trigger", + "threshold_z": "trigger" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..d066f5c3 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "03CF00000000282307" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..5e803590 --- /dev/null +++ b/VENDORS/Milesight/EM310-TILT/LORIOT/uplink/result_1.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": " EM310-TILT", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "angle_x": 0.0, + "angle_y": 0.0, + "angle_z": 90.0, + "threshold_x": "trigger", + "threshold_y": "trigger", + "threshold_z": "trigger" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/HTTP/uplink/converter.json b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/converter.json new file mode 100644 index 00000000..1b6f2676 --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM310-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM310-UDL \" + data.devEUI;\nvar deviceType = \"EM310-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DISTANCE\n else if (channel_id === 0x03 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/HTTP/uplink/payload.json b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/payload.json new file mode 100644 index 00000000..1202f549 --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVcA4JECAQAAQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/HTTP/uplink/result.json b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/result.json new file mode 100644 index 00000000..5df65dfc --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "EM310-UDL 24e1641092176759", + "deviceType": "EM310-UDL", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 92, + "distance": 2116, + "position": "tilt" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..f84d62f4 --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01755C03824408040001" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..883672ba --- /dev/null +++ b/VENDORS/Milesight/EM310-UDL/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM310-UDL", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 92, + "distance": 2116, + "position": "tilt" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/HTTP/uplink/converter.json b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/converter.json new file mode 100644 index 00000000..40b0f033 --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM320-TILT", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM320-TILT \" + data.devEUI;\nvar deviceType = \"EM320-TILT\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // ANGLE\n else if (channel_id === 0x03 && channel_type === 0xd4) {\n decoded.angle_x = (((input[i] & 0xFF) | (input[i + 1] << 8)) >> 1) / 100;\n decoded.angle_y = (((input[i + 2] & 0xFF) | (input[i + 3] << 8)) >> 1) / 100;\n decoded.angle_z = (((input[i + 4] & 0xFF) | (input[i + 5] << 8)) >> 1) / 100;\n decoded.threshold_x = (input[i] & 0x01) === 0x01 ? \"trigger\" : \"normal\";\n decoded.threshold_y = (input[i + 2] & 0x01) === 0x01 ? \"trigger\" : \"normal\";\n decoded.threshold_z = (input[i + 4] & 0x01) === 0x01 ? \"trigger\" : \"normal\";\n i += 6;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/HTTP/uplink/payload.json b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/payload.json new file mode 100644 index 00000000..ffc45489 --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA9QAAAEAUEY=" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/HTTP/uplink/result.json b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/result.json new file mode 100644 index 00000000..2ee3b947 --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/HTTP/uplink/result.json @@ -0,0 +1,24 @@ +{ + "deviceName": "EM320-TILT 24e1641092176759", + "deviceType": "EM320-TILT", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "angle_x": 0, + "angle_y": 0, + "angle_z": 90, + "threshold_x": "normal", + "threshold_y": "trigger", + "threshold_z": "normal" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..9587eeb3 --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01756403D4000001005046" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..afa611ef --- /dev/null +++ b/VENDORS/Milesight/EM320-TILT/LORIOT/uplink/result_1.json @@ -0,0 +1,47 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM320-TILT", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "angle_x": 0, + "angle_y": 0, + "angle_z": 90, + "threshold_x": "normal", + "threshold_y": "trigger", + "threshold_z": "normal" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-MUD/info.json b/VENDORS/Milesight/EM400-MUD/info.json deleted file mode 100644 index a3e5ee6f..00000000 --- a/VENDORS/Milesight/EM400-MUD/info.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "url": "https://www.milesight.com/iot/product/lorawan-sensor/em400-mud", - "label": "EM400-MUD: Multifunctional Ultrasonic Distance Sensor", - "description": "EM400-MUD is a multifunctional ultrasonic distance sensor designed to detect small-range areas and small blind spots. It features switchable pre-set modes for different applications. EM400-MUD is also equipped with a 3-axis accelerometer and temperature sensor to detect device status. " -} - diff --git a/VENDORS/Milesight/EM400-MUD/photo.png b/VENDORS/Milesight/EM400-MUD/photo.png deleted file mode 100644 index 83c128c1..00000000 Binary files a/VENDORS/Milesight/EM400-MUD/photo.png and /dev/null differ diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/converter.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/converter.json similarity index 98% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/converter.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/converter.json index 76438d0f..37e8440b 100644 --- a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/converter.json +++ b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/converter.json @@ -1,5 +1,5 @@ { - "name": "ChirpStack Uplink Decoder for EM400-MUD", + "name": "ChirpStack Uplink Decoder for EM400-TLD", "type": "UPLINK", "debugMode": false, "debugSettings": { @@ -10,7 +10,7 @@ "configuration": { "scriptLang": "TBEL", "decoder": null, - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"EM400-MUD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"EM400-TLD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/metadata.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/metadata.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/metadata.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/metadata.json diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/payload.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/payload.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/payload.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/payload.json diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/payload_1.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/payload_1.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/payload_1.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/payload_1.json diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result.json similarity index 96% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result.json index 17e42a98..075f978a 100644 --- a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result.json +++ b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "Device name 1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "devAddr": "20000001", diff --git a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result_1.json b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result_1.json similarity index 96% rename from VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result_1.json rename to VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result_1.json index 95b95f33..fa25878e 100644 --- a/VENDORS/Milesight/EM400-MUD/ChirpStack/uplink/result_1.json +++ b/VENDORS/Milesight/EM400-TLD/ChirpStack/uplink/result_1.json @@ -1,6 +1,6 @@ { "deviceName": "Device name 1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "devAddr": "20000001", diff --git a/VENDORS/Milesight/EM400-TLD/HTTP/uplink/converter.json b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/converter.json new file mode 100644 index 00000000..94042d51 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM400-TLD", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM400-TLD \" + data.devEUI;\nvar deviceType = \"EM400-TLD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-TLD/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-TLD/HTTP/uplink/payload.json b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/payload.json new file mode 100644 index 00000000..3fe39aa1 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "g2foAAGEgkEGAQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-TLD/HTTP/uplink/result.json b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/result.json new file mode 100644 index 00000000..888add00 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/HTTP/uplink/result.json @@ -0,0 +1,21 @@ +{ + "deviceName": "EM400-TLD 24e1641092176759", + "deviceType": "EM400-TLD", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "temperature": 23.2, + "temperature_abnormal": true, + "distance": 1601, + "distance_alarming": true + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/converter.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/converter.json similarity index 98% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/converter.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/converter.json index fa75430a..44422245 100644 --- a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/converter.json @@ -1,5 +1,5 @@ { - "name": "Loriot Uplink Decoder for EM400-MUD", + "name": "Loriot Uplink Decoder for EM400-TLD", "type": "UPLINK", "debugMode": false, "debugSettings": { @@ -10,7 +10,7 @@ "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"EM400-MUD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"EM400-TLD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/metadata.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/metadata.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/metadata.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/metadata.json diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/payload.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/payload.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload.json diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload_1.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/payload_1.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload_1.json diff --git a/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4e7f19ff --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01755C0367010104824408050001" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result.json similarity index 91% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result.json index 6961bcfc..a2412303 100644 --- a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result.json +++ b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result.json @@ -1,6 +1,6 @@ [{ "deviceName": "1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_1.json similarity index 91% rename from VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result_1.json rename to VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_1.json index fc5481c9..21f8b330 100644 --- a/VENDORS/Milesight/EM400-MUD/LORIOT/uplink/result_1.json +++ b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_1.json @@ -1,6 +1,6 @@ [{ "deviceName": "1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_2.json b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..7670f3b6 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/LORIOT/uplink/result_2.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM400-TLD", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/converter.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/converter.json similarity index 98% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/converter.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/converter.json index 281b7222..74b384df 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/converter.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/converter.json @@ -1,5 +1,5 @@ { - "name": "The Things Stack Community Uplink Decoder for EM400-MUD", + "name": "The Things Stack Community Uplink Decoder for EM400-TLD", "type": "UPLINK", "debugMode": false, "debugSettings": { @@ -10,7 +10,7 @@ "configuration": { "scriptLang": "TBEL", "decoder": null, - "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-MUD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-TLD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/metadata.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/metadata.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/metadata.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/payload.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/payload.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/payload.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/payload.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/payload_1.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/payload_1.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/payload_1.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/payload_1.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result.json similarity index 95% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result.json index 11b7016b..81491dfb 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "eui-1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result_1.json b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result_1.json similarity index 95% rename from VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result_1.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result_1.json index 845fad27..9cb5688c 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackCommunity/uplink/result_1.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackCommunity/uplink/result_1.json @@ -1,6 +1,6 @@ { "deviceName": "eui-1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/converter.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/converter.json similarity index 99% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/converter.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/converter.json index 3c0e197d..a3f57163 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/converter.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/converter.json @@ -1,5 +1,5 @@ { - "name": "The Things Stack Industries Uplink Decoder for EM400-MUD", + "name": "The Things Stack Industries Uplink Decoder for EM400-TLD", "type": "UPLINK", "debugMode": false, "debugSettings": { @@ -10,7 +10,7 @@ "configuration": { "scriptLang": "TBEL", "decoder": null, - "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-MUD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-TLD\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/metadata.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/metadata.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/metadata.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/payload.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/payload.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/payload.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/payload.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/payload_1.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/payload_1.json similarity index 100% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/payload_1.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/payload_1.json diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result.json similarity index 95% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result.json index 3a7a7c98..ffca8352 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result.json @@ -1,6 +1,6 @@ { "deviceName": "eui-1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result_1.json b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result_1.json similarity index 95% rename from VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result_1.json rename to VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result_1.json index ea05fca1..8d87d44e 100644 --- a/VENDORS/Milesight/EM400-MUD/ThingsStackIndustries/uplink/result_1.json +++ b/VENDORS/Milesight/EM400-TLD/ThingsStackIndustries/uplink/result_1.json @@ -1,6 +1,6 @@ { "deviceName": "eui-1000000000000001", - "deviceType": "EM400-MUD", + "deviceType": "EM400-TLD", "attributes": { "eui": "1000000000000001", "fPort": 85, diff --git a/VENDORS/Milesight/EM400-MUD/guide.md b/VENDORS/Milesight/EM400-TLD/guide.md similarity index 96% rename from VENDORS/Milesight/EM400-MUD/guide.md rename to VENDORS/Milesight/EM400-TLD/guide.md index 7d9ebac4..9b39c828 100644 --- a/VENDORS/Milesight/EM400-MUD/guide.md +++ b/VENDORS/Milesight/EM400-TLD/guide.md @@ -1,6 +1,6 @@ # Multifunctional Ultrasonic Distance/Level Sensor - Milesight IoT -The payload decoder function is applicable to EM400-MUD. +The payload decoder function is applicable to EM400-TLD. ## Payload Definition diff --git a/VENDORS/Milesight/EM400-TLD/info.json b/VENDORS/Milesight/EM400-TLD/info.json new file mode 100644 index 00000000..9053f874 --- /dev/null +++ b/VENDORS/Milesight/EM400-TLD/info.json @@ -0,0 +1,6 @@ +{ + "url": "https://www.milesight.com/iot/product/lorawan-sensor/em400-tld", + "label": "EM400-TLD ToF Laser Distance Sensor", + "description": "EM400-TLD is a distance sensor based on ToF (time of flight), which is mainly used for detecting the fill level and position status. With an appropriate FOV with the maximum field angle of 27°, it has almost no blind spot when installed on small-sized waste bins or containers." +} + diff --git a/VENDORS/Milesight/EM400-TLD/photo.png b/VENDORS/Milesight/EM400-TLD/photo.png new file mode 100644 index 00000000..336f7dea Binary files /dev/null and b/VENDORS/Milesight/EM400-TLD/photo.png differ diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/converter.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..b6815aff --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for EM400-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"EM400-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/metadata.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..3e3584b0 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 85, + "confirmed": false, + "data": "g2foAAGEgkEGAQ==", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload_1.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload_1.json new file mode 100644 index 00000000..154ca2e8 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/payload_1.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 85, + "confirmed": false, + "data": "AXVcA2cBAQSCRAgFAAE=", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result.json new file mode 100644 index 00000000..62d3cea5 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 85, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "temperature": 23.2, + "temperature_abnormal": true, + "distance": 1601, + "distance_alarming": true + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result_1.json b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result_1.json new file mode 100644 index 00000000..a943d1bf --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ChirpStack/uplink/result_1.json @@ -0,0 +1,28 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 85, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/HTTP/uplink/converter.json b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/converter.json new file mode 100644 index 00000000..0f6c53c9 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM400-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM400-UDL \" + data.devEUI;\nvar deviceType = \"EM400-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/HTTP/uplink/payload.json b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/payload.json new file mode 100644 index 00000000..b559a7d6 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVcA2cBAQSCRAgFAAE=" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/HTTP/uplink/result.json b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/result.json new file mode 100644 index 00000000..34bba608 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/HTTP/uplink/result.json @@ -0,0 +1,21 @@ +{ + "deviceName": "EM400-UDL 24e1641092176759", + "deviceType": "EM400-UDL", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/converter.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/converter.json new file mode 100644 index 00000000..8540d54c --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for EM400-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"EM400-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/metadata.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload.json new file mode 100644 index 00000000..95879188 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 85, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "8367e800018482410601" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..ef349fd0 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_1.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 85, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "01755C0367010104824408050001" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..4e7f19ff --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01755C0367010104824408050001" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result.json new file mode 100644 index 00000000..84a6b87f --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result.json @@ -0,0 +1,18 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "temperature": 23.2, + "temperature_abnormal": true, + "distance": 1601, + "distance_alarming": true + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..21cb2eea --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_1.json @@ -0,0 +1,18 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_2.json b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..da1342a4 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/LORIOT/uplink/result_2.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/converter.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..26cc9e5b --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for EM400-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..281d8f45 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 85, + "f_cnt": 10335, + "frm_payload": "g2foAAGEgkEGAQ==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload_1.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload_1.json new file mode 100644 index 00000000..76ae9bd5 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/payload_1.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 85, + "f_cnt": 10335, + "frm_payload": "AXVcA2cBAQSCRAgFAAE=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..68147d48 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "temperature": 23.2, + "temperature_abnormal": true, + "distance": 1601, + "distance_alarming": true + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result_1.json b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result_1.json new file mode 100644 index 00000000..ffeeafe0 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackCommunity/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/converter.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..b275fc93 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for EM400-UDL", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM400-UDL\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // DISTANCE\n else if (channel_id === 0x04 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // POSITION\n else if (channel_id === 0x05 && channel_type === 0x00) {\n decoded.position = input[i] === 0 ? \"normal\" : \"tilt\";\n i += 1;\n }\n // TEMPERATURE WITH ABNORMAL\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_abnormal = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n // DISTANCE WITH ALARMING\n else if (channel_id === 0x84 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n decoded.distance_alarming = input[i + 2] == 0 ? false : true;\n i += 3;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..904c0fa0 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Industries integration new", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..1763bfd0 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 85, + "f_cnt": 5017, + "frm_payload": "g2foAAGEgkEGAQ==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload_1.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload_1.json new file mode 100644 index 00000000..84cc5f8f --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/payload_1.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 85, + "f_cnt": 5017, + "frm_payload": "AXVcA2cBAQSCRAgFAAE=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..b997733c --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "temperature": 23.2, + "temperature_abnormal": true, + "distance": 1601, + "distance_alarming": true + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result_1.json b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result_1.json new file mode 100644 index 00000000..75ff5e26 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/ThingsStackIndustries/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM400-UDL", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "battery": 92, + "temperature": 25.7, + "distance": 2116, + "position": "tilt" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/guide.md b/VENDORS/Milesight/EM400-UDL/guide.md new file mode 100644 index 00000000..56807e5e --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/guide.md @@ -0,0 +1,21 @@ +# Multifunctional Ultrasonic Distance/Level Sensor - Milesight IoT + +The payload decoder function is applicable to EM400-UDL. + +## Payload Definition + +| CHANNEL | ID | TYPE | LENGTH | DESCRIPTION | +| :--------------------: | :--: | :--: | :----: | ------------------------------------------------------ | +| Battery | 0x01 | 0x75 | 1 | battery(1B)
battery, unit: % | +| Temperature | 0x03 | 0x67 | 2 | temperature(2B)
temperature, unit: ℃ | +| Distance | 0x04 | 0x82 | 2 | distance(2B)
distance, unit: mm | +| Position | 0x05 | 0x00 | 1 | position(1B)
position, values(0: normal, 1: tilt) | +| Location
(NB-IoT) | 0x06 | 0x88 | 9 | longitude(4B) + latitude(4B) + motion_status(1B) | +| Temperature Abnormal | 0x83 | 0x67 | 3 | temperature(2B) + status(1B) | +| Distance Alarm | 0x84 | 0x82 | 3 | distance(2B) + status(1B) | + +### Motion Status Definition + +| BITS | 7..4 | 3..0 | +| :---------: | :-------------------------------------------------------------------- | :------------------------------------------------------------------------------ | +| DESCRIPTION | geofence_status, values: (0: inside, 1: outside, 2: unset, 3: unknown | motion_status, values: (0: unknown, 1: start moving, 2: moving, 3: stop moving) | \ No newline at end of file diff --git a/VENDORS/Milesight/EM400-UDL/info.json b/VENDORS/Milesight/EM400-UDL/info.json new file mode 100644 index 00000000..f36ad2f8 --- /dev/null +++ b/VENDORS/Milesight/EM400-UDL/info.json @@ -0,0 +1,6 @@ +{ + "url": "https://www.milesight.com/iot/product/lorawan-sensor/em400-udl", + "label": "EM400-UDL Ultrasonic Distance Sensor", + "description": "EM400-UDL is a non-contact ultrasonic distance sensor designed for outdoor applications such as monitoring water level, fill level of tanks and silos, presence of objects, or snow level." +} + diff --git a/VENDORS/Milesight/EM400-UDL/photo.png b/VENDORS/Milesight/EM400-UDL/photo.png new file mode 100644 index 00000000..974c3fe8 Binary files /dev/null and b/VENDORS/Milesight/EM400-UDL/photo.png differ diff --git a/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/converter.json b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..e802bdfc --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for EM500-C02", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"EM500-C02\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n else if (channel_id === 0x06 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // TEMPERATURE CHANGE ALARM\n else if (channel_id === 0x83 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTROY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n co2: parseBytesToInt(input, i + 4, 2, false),\n pressure: parseBytesToInt(input, i + 6, 1, false) / 10,\n temperature: parseBytesToInt(input, i + 8, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 10, 1, false, false) / 2\n }\n };\n i += 11;\n \n historyDataList.add(historyData);\n } \n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm\";\n case 1:\n return \"threshold alarm release\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/metadata.json b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/payload.json b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..88086852 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 85, + "confirmed": false, + "data": "AXVkA2cZAQRocwV9ZwQGc2gn", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/result.json b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/result.json new file mode 100644 index 00000000..84dcc988 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ChirpStack/uplink/result.json @@ -0,0 +1,29 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "EM500-C02", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 85, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/HTTP/uplink/converter.json b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/converter.json new file mode 100644 index 00000000..de2f47c1 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for EM500-C02", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"EM500-C02 \" + data.devEUI;\nvar deviceType = \"EM500-C02\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n else if (channel_id === 0x06 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // TEMPERATURE CHANGE ALARM\n else if (channel_id === 0x83 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTROY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n co2: parseBytesToInt(input, i + 4, 2, false),\n pressure: parseBytesToInt(input, i + 6, 1, false) / 10,\n temperature: parseBytesToInt(input, i + 8, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 10, 1, false, false) / 2\n }\n };\n i += 11;\n \n historyDataList.add(historyData);\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm\";\n case 1:\n return \"threshold alarm release\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/HTTP/uplink/metadata.json b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/HTTP/uplink/payload.json b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/payload.json new file mode 100644 index 00000000..0cca0e50 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA2cZAQRocwV9ZwQGc2gn" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/HTTP/uplink/result.json b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/result.json new file mode 100644 index 00000000..82a4dda7 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/HTTP/uplink/result.json @@ -0,0 +1,22 @@ +{ + "deviceName": "EM500-C02 24e1641092176759", + "deviceType": "EM500-C02", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/converter.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/converter.json new file mode 100644 index 00000000..0e50e061 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for EM500-C02", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"EM500-C02\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n else if (channel_id === 0x06 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // TEMPERATURE CHANGE ALARM\n else if (channel_id === 0x83 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTROY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n co2: parseBytesToInt(input, i + 4, 2, false),\n pressure: parseBytesToInt(input, i + 6, 1, false) / 10,\n temperature: parseBytesToInt(input, i + 8, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 10, 1, false, false) / 2\n }\n };\n i += 11;\n \n historyDataList.add(historyData);\n } \n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm\";\n case 1:\n return \"threshold alarm release\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/metadata.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload.json new file mode 100644 index 00000000..0bbb8113 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 85, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "01756403671901046873057D670406736827" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..ce781b81 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01756403671901046873057D670406736827" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result.json new file mode 100644 index 00000000..08bd685b --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result.json @@ -0,0 +1,19 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "EM500-C02", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result_1.json b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..883ede5b --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/LORIOT/uplink/result_1.json @@ -0,0 +1,45 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "EM500-C02", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/converter.json b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..6a810945 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for EM500-C02", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM500-C02\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n else if (channel_id === 0x06 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // TEMPERATURE CHANGE ALARM\n else if (channel_id === 0x83 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTROY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n co2: parseBytesToInt(input, i + 4, 2, false),\n pressure: parseBytesToInt(input, i + 6, 1, false) / 10,\n temperature: parseBytesToInt(input, i + 8, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 10, 1, false, false) / 2\n }\n };\n i += 11;\n \n historyDataList.add(historyData);\n } \n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm\";\n case 1:\n return \"threshold alarm release\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/payload.json b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..db5532f6 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 85, + "f_cnt": 10335, + "frm_payload": "AXVkA2cZAQRocwV9ZwQGc2gn", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/result.json b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..ac2cd036 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,30 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM500-C02", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/converter.json b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..966af784 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for EM500-C02", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"EM500-C02\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var historyDataList = [];\n for (var i = 0; i < input.length - 2 ; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x04 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // CO2\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.co2 = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // PRESSURE\n else if (channel_id === 0x06 && channel_type === 0x73) {\n decoded.pressure = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n }\n // TEMPERATURE CHANGE ALARM\n else if (channel_id === 0x83 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTROY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var historyData = {\n ts: parseBytesToInt(input, i, 4, false) * 1000,\n values: {\n co2: parseBytesToInt(input, i + 4, 2, false),\n pressure: parseBytesToInt(input, i + 6, 1, false) / 10,\n temperature: parseBytesToInt(input, i + 8, 2, false) / 10,\n humidity: parseBytesToInt(input, i + 10, 1, false, false) / 2\n }\n };\n i += 11;\n \n historyDataList.add(historyData);\n } \n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm\";\n case 1:\n return \"threshold alarm release\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..904c0fa0 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Industries integration new", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/payload.json b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..798b317e --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 85, + "f_cnt": 5017, + "frm_payload": "AXVkA2cZAQRocwV9ZwQGc2gn", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/result.json b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..2c54a417 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,30 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "EM500-C02", + "attributes": { + "eui": "1000000000000001", + "fPort": 85, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "battery": 100, + "temperature": 28.1, + "humidity": 57.5, + "co2": 1127, + "pressure": 1008.8 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/guide.md b/VENDORS/Milesight/EM500-CO2/guide.md new file mode 100644 index 00000000..e84fdf21 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/guide.md @@ -0,0 +1,15 @@ +# CO2 Sensor - Milesight IoT + +The payload decoder function is applicable to EM500-CO2. + +## Payload Definition + +| CHANNEL | ID | TYPE | LENGTH | DESCRIPTION | +| :---------------: | :--: | :--: | :----: | ----------------------------------------------------------------------------------------- | +| Battery | 0x01 | 0x75 | 1 | battery(1B)
battery, unit: % | +| Temperature | 0x03 | 0x67 | 2 | temperature(2B)
temperature,unit: ℃ | +| Humidity | 0x04 | 0x68 | 1 | humidity(1B)
humidity, unit: %RH | +| CO2 | 0x05 | 0x7D | 2 | co2(2B)
co2, unit: ppm | +| Pressure | 0x06 | 0x73 | 2 | pressure(2B)
pressure, unit: hPa | +| Temperature Alarm | 0x83 | 0xD7 | 5 | temperature(2B) + temperature_change(2B) + temperature_alarm(1B)
temperature, unit: ℃ | +| Historical Data | 0x20 | 0XCE | 11 | timestamp(4B) + co2(2B) + pressure(2B) + temperature(2B) + humidity(1B) | \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/info.json b/VENDORS/Milesight/EM500-CO2/info.json new file mode 100644 index 00000000..9e9bbf75 --- /dev/null +++ b/VENDORS/Milesight/EM500-CO2/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://www.milesight.com/iot/product/lorawan-sensor/em500-co2", + "label": "EM500-C02: LoRaWAN® Carbon Dioxide sensor", + "description": "EM500-CO2 is designed for measuring CO2, temperature, humidity and barometric pressure in harsh environments and transmitting data using LoRaWAN® technology." +} \ No newline at end of file diff --git a/VENDORS/Milesight/EM500-CO2/photo.png b/VENDORS/Milesight/EM500-CO2/photo.png new file mode 100644 index 00000000..74ff8953 Binary files /dev/null and b/VENDORS/Milesight/EM500-CO2/photo.png differ diff --git a/VENDORS/Milesight/GS101/HTTP/uplink/converter.json b/VENDORS/Milesight/GS101/HTTP/uplink/converter.json new file mode 100644 index 00000000..f61a137c --- /dev/null +++ b/VENDORS/Milesight/GS101/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for GS101", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS101 \" + data.devEUI;\nvar deviceType = \"GS101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // GAS STATUS\n if (channel_id === 0x05 && channel_type === 0x8e) {\n decoded.state = input[i] === 0 ? \"normal\" : \"abnormal\";\n i += 1;\n }\n // VALVE\n if (channel_id === 0x06 && channel_type === 0x01) {\n decoded.valve = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // RELAY\n if (channel_id === 0x07 && channel_type === 0x01) {\n decoded.relay = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // REMAINED LIFE TIME\n if (channel_id === 0x08 && channel_type === 0x90) {\n decoded.life_remain = parseBytesToInt(input, i, 4, false);\n i += 4;\n }\n // ALARM\n else if (channel_id === 0xff && channel_type === 0x3f) {\n var alarm_type = input[i];\n i += 1;\n decoded.alarm = decodeAlarmType(alarm_type);\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction decodeAlarmType(alarm_type) {\n if (alarm_type === 0) {\n return \"power down\";\n } else if (alarm_type === 1) {\n return \"power on\";\n } else if (alarm_type === 2) {\n return \"sensor failure\";\n } else if (alarm_type === 3) {\n return \"sensor recover\";\n } else if (alarm_type === 4) {\n return \"sensor about to fail\";\n } else if (alarm_type === 5) {\n return \"sensor failed\";\n } else {\n return \"unknown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS101/HTTP/uplink/metadata.json b/VENDORS/Milesight/GS101/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/GS101/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS101/HTTP/uplink/payload.json b/VENDORS/Milesight/GS101/HTTP/uplink/payload.json new file mode 100644 index 00000000..8e075050 --- /dev/null +++ b/VENDORS/Milesight/GS101/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "BY4ABgEBBwEA" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS101/HTTP/uplink/result.json b/VENDORS/Milesight/GS101/HTTP/uplink/result.json new file mode 100644 index 00000000..6f8cf3ee --- /dev/null +++ b/VENDORS/Milesight/GS101/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "GS101 24e1641092176759", + "deviceType": "GS101", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "state": "normal", + "valve": "open", + "relay": "close" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS101/LORIOT/uplink/converter.json b/VENDORS/Milesight/GS101/LORIOT/uplink/converter.json index 85ab36ac..e5c3889a 100644 --- a/VENDORS/Milesight/GS101/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/GS101/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for GS101", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735211336948 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS 101 \" + data.EUI;\nvar deviceType = \"Gas sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length -2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // GAS STATUS\n if (channel_id === 0x05 && channel_type === 0x8e) {\n decoded.state = input[i] === 0 ? \"normal\" : \"abnormal\";\n i += 1;\n }\n // VALVE\n if (channel_id === 0x06 && channel_type === 0x01) {\n decoded.valve = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // RELAY\n if (channel_id === 0x07 && channel_type === 0x01) {\n decoded.relay = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // REMAINED LIFE TIME\n if (channel_id === 0x08 && channel_type === 0x90) {\n decoded.life_remain = parseBytesToInt(input, i, 4, false);\n i += 4;\n }\n // ALARM\n else if (channel_id === 0xff && channel_type === 0x3f) {\n var alarm_type = input[i];\n i += 1;\n decoded.alarm = decodeAlarmType(alarm_type);\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction calculatePreviousState(compressedDuration) {\n var state = {};\n state.previous_state_duration_overflow = false;\n\n if (compressedDuration < 90) {\n state.previous_state_duration = compressedDuration;\n state.previous_state_duration_error = 0;\n } else if (compressedDuration >= 90 && compressedDuration < 120) {\n state.previous_state_duration = 90 + (compressedDuration - 90) * 5;\n state.previous_state_duration_error = 4;\n } else if (compressedDuration >= 120 && compressedDuration < 127) {\n result.previous_state_duration = 240 + (compressedDuration - 120) * 60;\n result.previous_state_duration_error = 59;\n } else if (compressedDuration === 127) {\n state.previous_state_duration = 660;\n state.previous_state_duration_error = null;\n state.previous_state_duration_overflow = true;\n }\n\n return state;\n }", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS 101 \" + data.EUI;\nvar deviceType = \"Gas sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length -2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // GAS STATUS\n if (channel_id === 0x05 && channel_type === 0x8e) {\n decoded.state = input[i] === 0 ? \"normal\" : \"abnormal\";\n i += 1;\n }\n // VALVE\n if (channel_id === 0x06 && channel_type === 0x01) {\n decoded.valve = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // RELAY\n if (channel_id === 0x07 && channel_type === 0x01) {\n decoded.relay = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // REMAINED LIFE TIME\n if (channel_id === 0x08 && channel_type === 0x90) {\n decoded.life_remain = parseBytesToInt(input, i, 4, false);\n i += 4;\n }\n // ALARM\n else if (channel_id === 0xff && channel_type === 0x3f) {\n var alarm_type = input[i];\n i += 1;\n decoded.alarm = decodeAlarmType(alarm_type);\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction calculatePreviousState(compressedDuration) {\n var state = {};\n state.previous_state_duration_overflow = false;\n\n if (compressedDuration < 90) {\n state.previous_state_duration = compressedDuration;\n state.previous_state_duration_error = 0;\n } else if (compressedDuration >= 90 && compressedDuration < 120) {\n state.previous_state_duration = 90 + (compressedDuration - 90) * 5;\n state.previous_state_duration_error = 4;\n } else if (compressedDuration >= 120 && compressedDuration < 127) {\n result.previous_state_duration = 240 + (compressedDuration - 120) * 60;\n result.previous_state_duration_error = 59;\n } else if (compressedDuration === 127) {\n state.previous_state_duration = 660;\n state.previous_state_duration_error = null;\n state.previous_state_duration_overflow = true;\n }\n\n return state;\n }", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/GS101/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/GS101/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..2d3a200b --- /dev/null +++ b/VENDORS/Milesight/GS101/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "058e00060101070100" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS101/LORIOT/uplink/result_1.json b/VENDORS/Milesight/GS101/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..027072d1 --- /dev/null +++ b/VENDORS/Milesight/GS101/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "GS 101 0102030405060708", + "deviceType": "Gas sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "state": "normal", + "valve": "open", + "relay": "close" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/HTTP/uplink/converter.json b/VENDORS/Milesight/GS301/HTTP/uplink/converter.json new file mode 100644 index 00000000..ab5b7069 --- /dev/null +++ b/VENDORS/Milesight/GS301/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for GS301", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS301 \" + data.devEUI;\nvar deviceType = \"Bathroom Odor Sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x02 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n \n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x03 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // NH3\n else if (channel_id === 0x04 && channel_type === 0x7d) {\n decoded.nh3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // H2S\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.h2s = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction decodeAlarmType(alarm_type) {\n if (alarm_type === 0) {\n return \"power down\";\n } else if (alarm_type === 1) {\n return \"power on\";\n } else if (alarm_type === 2) {\n return \"sensor failure\";\n } else if (alarm_type === 3) {\n return \"sensor recover\";\n } else if (alarm_type === 4) {\n return \"sensor about to fail\";\n } else if (alarm_type === 5) {\n return \"sensor failed\";\n } else {\n return \"unknown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/HTTP/uplink/metadata.json b/VENDORS/Milesight/GS301/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/GS301/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/HTTP/uplink/payload.json b/VENDORS/Milesight/GS301/HTTP/uplink/payload.json new file mode 100644 index 00000000..b26a6123 --- /dev/null +++ b/VENDORS/Milesight/GS301/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkAmccAQNoZAR9AAAFfQEA" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/HTTP/uplink/result.json b/VENDORS/Milesight/GS301/HTTP/uplink/result.json new file mode 100644 index 00000000..45018113 --- /dev/null +++ b/VENDORS/Milesight/GS301/HTTP/uplink/result.json @@ -0,0 +1,22 @@ +{ + "deviceName": "GS301 24e1641092176759", + "deviceType": "Bathroom Odor Sensor", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "temperature": 28.4, + "humidity": 50.0, + "nh3": 0.0, + "h2s": 0.01 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/LORIOT/uplink/converter.json b/VENDORS/Milesight/GS301/LORIOT/uplink/converter.json index bdc3cc55..1eaa34bd 100644 --- a/VENDORS/Milesight/GS301/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/GS301/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for GS301", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735211457581 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS 301 \" + data.EUI;\nvar deviceType = \"Bathroom Odor Sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length -2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x02 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x03 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // NH3\n else if (channel_id === 0x04 && channel_type === 0x7d) {\n decoded.nh3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // H2S\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.h2s = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction calculatePreviousState(compressedDuration) {\n var state = {};\n state.previous_state_duration_overflow = false;\n\n if (compressedDuration < 90) {\n state.previous_state_duration = compressedDuration;\n state.previous_state_duration_error = 0;\n } else if (compressedDuration >= 90 && compressedDuration < 120) {\n state.previous_state_duration = 90 + (compressedDuration - 90) * 5;\n state.previous_state_duration_error = 4;\n } else if (compressedDuration >= 120 && compressedDuration < 127) {\n result.previous_state_duration = 240 + (compressedDuration - 120) * 60;\n result.previous_state_duration_error = 59;\n } else if (compressedDuration === 127) {\n state.previous_state_duration = 660;\n state.previous_state_duration_error = null;\n state.previous_state_duration_overflow = true;\n }\n\n return state;\n }", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"GS 301 \" + data.EUI;\nvar deviceType = \"Bathroom Odor Sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n for (var i = 0; i < input.length -2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x02 && channel_type === 0x67) {\n // ℃\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10;\n i += 2;\n\n // ℉\n // decoded.temperature = parseBytesToInt(input, i, 2, false) / 10 * 1.8 + 32;\n // i +=2;\n }\n // HUMIDITY\n else if (channel_id === 0x03 && channel_type === 0x68) {\n decoded.humidity = parseBytesToInt(input, i, 1, false) / 2;\n i += 1;\n }\n // NH3\n else if (channel_id === 0x04 && channel_type === 0x7d) {\n decoded.nh3 = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n // H2S\n else if (channel_id === 0x05 && channel_type === 0x7d) {\n decoded.h2s = parseBytesToInt(input, i, 2, false) / 100;\n i += 2;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction calculatePreviousState(compressedDuration) {\n var state = {};\n state.previous_state_duration_overflow = false;\n\n if (compressedDuration < 90) {\n state.previous_state_duration = compressedDuration;\n state.previous_state_duration_error = 0;\n } else if (compressedDuration >= 90 && compressedDuration < 120) {\n state.previous_state_duration = 90 + (compressedDuration - 90) * 5;\n state.previous_state_duration_error = 4;\n } else if (compressedDuration >= 120 && compressedDuration < 127) {\n result.previous_state_duration = 240 + (compressedDuration - 120) * 60;\n result.previous_state_duration_error = 59;\n } else if (compressedDuration === 127) {\n state.previous_state_duration = 660;\n state.previous_state_duration_error = null;\n state.previous_state_duration_overflow = true;\n }\n\n return state;\n }", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/GS301/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/GS301/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..10df41bc --- /dev/null +++ b/VENDORS/Milesight/GS301/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01756402671C01036864047D0000057D0100" +} \ No newline at end of file diff --git a/VENDORS/Milesight/GS301/LORIOT/uplink/result_1.json b/VENDORS/Milesight/GS301/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..2dca6b89 --- /dev/null +++ b/VENDORS/Milesight/GS301/LORIOT/uplink/result_1.json @@ -0,0 +1,45 @@ +[{ + "deviceName": "GS 301 0102030405060708", + "deviceType": "Bathroom Odor Sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "temperature": 28.4, + "humidity": 50.0, + "nh3": 0.0, + "h2s": 0.01 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/HTTP/uplink/converter.json b/VENDORS/Milesight/TS101/HTTP/uplink/converter.json new file mode 100644 index 00000000..c292d378 --- /dev/null +++ b/VENDORS/Milesight/TS101/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for TS101", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"TS101 \" + data.devEUI;\nvar deviceType = \"Smoke detector\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n i += 2;\n }\n // TEMPERATURE ALARM\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n // TEMPERATURE MUTATION ALERT\n else if (channel_id === 0x93 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 100;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTORY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n historyDate = {\n ts : parseBytesToInt(input, i, 4, false) * 1000,\n values : {\n temperature: parseBytesToInt(input, i + 4, 2) / 10\n }\n };\n \n historyDataList.add(historyDate);\n i += 6;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm release\";\n case 1:\n return \"threshold alarm\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/HTTP/uplink/metadata.json b/VENDORS/Milesight/TS101/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/TS101/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/HTTP/uplink/payload.json b/VENDORS/Milesight/TS101/HTTP/uplink/payload.json new file mode 100644 index 00000000..dc321fce --- /dev/null +++ b/VENDORS/Milesight/TS101/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "g2dSAQE=" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/HTTP/uplink/result.json b/VENDORS/Milesight/TS101/HTTP/uplink/result.json new file mode 100644 index 00000000..b4770acb --- /dev/null +++ b/VENDORS/Milesight/TS101/HTTP/uplink/result.json @@ -0,0 +1,19 @@ +{ + "deviceName": "TS101 24e1641092176759", + "deviceType": "Smoke detector", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "temperature": 33.8, + "temperature_alarm": "threshold alarm" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/LORIOT/uplink/converter.json b/VENDORS/Milesight/TS101/LORIOT/uplink/converter.json index 102ab55c..6f9068e3 100644 --- a/VENDORS/Milesight/TS101/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/TS101/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for TS101", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735211789013 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"TS101 \" + data.EUI;\nvar deviceType = \"Temperature Sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var historyDate = {};\n for (var i = 0; i < input.length; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n i += 2;\n }\n // TEMPERATURE ALARM\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n // TEMPERATURE MUTATION ALERT\n else if (channel_id === 0x93 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 100;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTORY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n historyDate = {\n ts : parseBytesToInt(input, i, 4, false) * 1000,\n values : {\n temperature: parseBytesToInt(input, i + 4, 2) / 10\n }\n };\n i += 6;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyDate];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm release\";\n case 1:\n return \"threshold alarm\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"TS101 \" + data.EUI;\nvar deviceType = \"Temperature Sensor\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var historyDate = {};\n for (var i = 0; i < input.length; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n i += 2;\n }\n // TEMPERATURE ALARM\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_alarm = readTemperatureAlarm(input[i + 2]);\n i += 3;\n }\n // TEMPERATURE MUTATION ALERT\n else if (channel_id === 0x93 && channel_type === 0xd7) {\n decoded.temperature = parseBytesToInt(input, i, 2, false) / 10; \n decoded.temperature_change = parseBytesToInt(input, i + 2, 2, false) / 100;\n decoded.temperature_alarm = readTemperatureAlarm(input[i + 4]);\n i += 5;\n }\n // HISTORY\n else if (channel_id === 0x20 && channel_type === 0xce) {\n historyDate = {\n ts : parseBytesToInt(input, i, 4, false) * 1000,\n values : {\n temperature: parseBytesToInt(input, i + 4, 2) / 10\n }\n };\n i += 6;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }, historyDate];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readTemperatureAlarm(type) {\n switch (type) {\n case 0:\n return \"threshold alarm release\";\n case 1:\n return \"threshold alarm\";\n case 2:\n return \"mutation alarm\";\n default:\n return \"unkown\";\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/TS101/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/TS101/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..a4d6f514 --- /dev/null +++ b/VENDORS/Milesight/TS101/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "8367520101" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS101/LORIOT/uplink/result_2.json b/VENDORS/Milesight/TS101/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..a4cf60a9 --- /dev/null +++ b/VENDORS/Milesight/TS101/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "TS101 0102030405060708", + "deviceType": "Temperature Sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "temperature": 33.8, + "temperature_alarm": "threshold alarm" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/HTTP/uplink/converter.json b/VENDORS/Milesight/TS201/HTTP/uplink/converter.json new file mode 100644 index 00000000..905a61fe --- /dev/null +++ b/VENDORS/Milesight/TS201/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for TS201", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"TS201 \" + data.devEUI;\nvar deviceType = \"Smoke detector\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var wifi = [];\n var fPort = data.fPort;\n var historyDataList = [];\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // IPSO VERSION\n if (channel_id === 0xff && channel_type === 0x01) {\n output.attributes.ipso_version = readProtocolVersion(input[i]);\n i += 1;\n }\n // HARDWARE VERSION\n else if (channel_id === 0xff && channel_type === 0x09) {\n output.attributes.hardware_version = readHardwareVersion(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // FIRMWARE VERSION\n else if (channel_id === 0xff && channel_type === 0x0a) {\n output.attributes.firmware_version = readFirmwareVersion(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // DEVICE STATUS\n else if (channel_id === 0xff && channel_type === 0x0b) {\n decoded.device_status = \"on\";\n i += 1;\n }\n // LORAWAN CLASS TYPE\n else if (channel_id === 0xff && channel_type === 0x0f) {\n output.attributes.lorawan_class = readLoRaWANType(input[i]);\n i += 1;\n }\n // SERIAL NUMBER\n else if (channel_id === 0xff && channel_type === 0x16) {\n output.attributes.sn = readSerialNumber(java.util.Arrays.copyOfRange(input, i, i + 8));\n i += 8;\n }\n // TSL VERSION\n else if (channel_id === 0xff && channel_type === 0xff) {\n output.attributes.tsl_version = readTslVersion(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // BATTERY\n else if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // TEMPERATURE\n else if (channel_id === 0x03 && channel_type === 0x67) {\n decoded.temperature = getTemperatureValue(java.util.Arrays.copyOfRange(input, i, i + 2));\n i += 2;\n }\n // TEMPERATURE THRESHOLD ALARM\n else if (channel_id === 0x83 && channel_type === 0x67) {\n decoded.temperature = getTemperatureValue(java.util.Arrays.copyOfRange(input, i, i + 2));\n decoded.temperature_alarm = readAlarmType(input[i + 2]);\n i += 3;\n }\n // TEMPERATURE MUTATION ALARM\n else if (channel_id === 0x93 && channel_type === 0x67) {\n decoded.temperature = getTemperatureValue(java.util.Arrays.copyOfRange(input, i, i + 2));\n decoded.temperature_mutation = parseBytesToInt(input, i + 2, 2, false) / 10;\n decoded.temperature_alarm = readAlarmType(input[i + 4]);\n i += 5;\n }\n // TEMPERATURE ERROR\n else if (channel_id === 0xb3 && channel_type === 0x67) {\n decoded.temperature_error = readErrorType(input[i]);\n i += 1;\n }\n // HISTORY DATA\n else if (channel_id === 0x20 && channel_type === 0xce) {\n var event = input[i + 4];\n var historyData = {\n ts : parseBytesToInt(input, i, 4, false) * 1000,\n values : {\n temperature : getTemperatureValue(java.util.Arrays.copyOfRange(input, i + 5, i + 7)),\n read_status : readStatus((event >>> 4) & 0x0f),\n event_type : readType(event & 0x0f)\n }\n };\n \n historyDataList.push(historyData);\n i += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n output.telemetry.addAll(historyDataList);\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getTemperatureValue(temperatureBytes) {\n var ref = parseBytesToInt(temperatureBytes, 0, 2, false);\n return (ref > 0x7fff ? ref - 0x10000 : ref) / 10;\n}\n\nfunction readProtocolVersion(bytes) {\n var major = (bytes & 0xf0) >> 4;\n var minor = bytes & 0x0f;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readHardwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = (bytes[1] & 0xff) >> 4;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readFirmwareVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = bytes[1] & 0xff;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readTslVersion(bytes) {\n var major = bytes[0] & 0xff;\n var minor = bytes[1] & 0xff;\n return \"v\" + major + \".\" + minor;\n}\n\nfunction readSerialNumber(bytes) {\n var temp = [];\n for (var idx = 0; idx < bytes.length; idx++) {\n temp.push((\"0\" + (bytes[idx] & 0xff).toString(16)).slice(-2));\n }\n return temp.join(\"\");\n}\n\nfunction readLoRaWANType(type) {\n switch (type) {\n case 0x00:\n return \"ClassA\";\n case 0x01:\n return \"ClassB\";\n case 0x02:\n return \"ClassC\";\n case 0x03:\n return \"ClassCtoB\";\n default:\n return \"Unknown\";\n }\n}\n\nfunction readAlarmType(type) {\n switch (type) {\n case 0x00:\n return \"Threshold Alarm Release\";\n case 0x01:\n return \"Threshold Alarm\";\n case 0x02:\n return \"Mutation Alarm\";\n default:\n return \"Unknown\";\n }\n}\n\nfunction readErrorType(type) {\n switch (type) {\n case 0x00:\n return \"Read Error\";\n case 0x01:\n return \"Overload\";\n default:\n return \"Unknown\";\n }\n}\n\nfunction readHistoryEvent(type) {\n switch (type) {\n case 0x00:\n return \"Time Update\";\n case 0x01:\n return \"Periodic\";\n case 0x02:\n return \"Alarm(Threshold or Mutation)\";\n case 0x03:\n return \"Alarm Release\";\n case 0x04:\n return \"Read Error\";\n case 0x05:\n return \"Overload\";\n default:\n return \"Unknown\";\n }\n}\n\nfunction readStatus(type) {\n switch (type) {\n case 0x00:\n return \"Success\";\n case 0x01:\n return \"Read Error\";\n case 0x02:\n return \"Overload\";\n default:\n return \"Unknown\";\n }\n}\n\nfunction readType(type) {\n switch (type) {\n case 0x00:\n return \"\";\n case 0x01:\n return \"Periodic\";\n case 0x02:\n return \"Alarm(Threshold or Mutation)\";\n case 0x03:\n return \"Alarm Release\";\n default:\n return \"Unknown\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/HTTP/uplink/metadata.json b/VENDORS/Milesight/TS201/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/TS201/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/HTTP/uplink/payload.json b/VENDORS/Milesight/TS201/HTTP/uplink/payload.json new file mode 100644 index 00000000..6e27f50b --- /dev/null +++ b/VENDORS/Milesight/TS201/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "IM7HmvpkAr3/" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/HTTP/uplink/result.json b/VENDORS/Milesight/TS201/HTTP/uplink/result.json new file mode 100644 index 00000000..810e2f8e --- /dev/null +++ b/VENDORS/Milesight/TS201/HTTP/uplink/result.json @@ -0,0 +1,23 @@ +{ + "deviceName": "TS201 24e1641092176759", + "deviceType": "Smoke detector", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": {} + }, { + "ts": 1694145223000, + "values": { + "temperature": -6.7, + "read_status": "Success", + "event_type": "Alarm(Threshold or Mutation)" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/LORIOT/uplink/payload_2.json b/VENDORS/Milesight/TS201/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..5f6f4ba0 --- /dev/null +++ b/VENDORS/Milesight/TS201/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "20cec79afa6402bdff" +} \ No newline at end of file diff --git a/VENDORS/Milesight/TS201/LORIOT/uplink/result_2.json b/VENDORS/Milesight/TS201/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..529ae28f --- /dev/null +++ b/VENDORS/Milesight/TS201/LORIOT/uplink/result_2.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "TS201", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": {} + }, { + "ts": 1694145223000, + "values": { + "temperature": -6.7, + "read_status": "Success", + "event_type": "Alarm(Threshold or Mutation)" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/HTTP/uplink/converter.json b/VENDORS/Milesight/WS101/HTTP/uplink/converter.json new file mode 100644 index 00000000..d2731e93 --- /dev/null +++ b/VENDORS/Milesight/WS101/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS101", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS101 \" + data.devEUI;\nvar deviceType = \"Smoke detector\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PRESS STATE\n else if (channel_id === 0xff && channel_type === 0x2e) {\n setPressState(decoded, input[i]);\n i += 1;\n } \n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS101/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS101/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/HTTP/uplink/payload.json b/VENDORS/Milesight/WS101/HTTP/uplink/payload.json new file mode 100644 index 00000000..769156f7 --- /dev/null +++ b/VENDORS/Milesight/WS101/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXUQ/y4B" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/HTTP/uplink/result.json b/VENDORS/Milesight/WS101/HTTP/uplink/result.json new file mode 100644 index 00000000..897d8079 --- /dev/null +++ b/VENDORS/Milesight/WS101/HTTP/uplink/result.json @@ -0,0 +1,19 @@ +{ + "deviceName": "WS101 24e1641092176759", + "deviceType": "Smoke detector", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 16, + "press": "short" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS101/LORIOT/uplink/converter.json index 3b1f6eb6..27cc17cc 100644 --- a/VENDORS/Milesight/WS101/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS101/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS101", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735212363893 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PRESS STATE\n else if (channel_id === 0xff && channel_type === 0x2e) {\n setPressState(decoded, input[i]);\n i += 1;\n } \n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS101\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PRESS STATE\n else if (channel_id === 0xff && channel_type === 0x2e) {\n setPressState(decoded, input[i]);\n i += 1;\n } \n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS101/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS101/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..5561fb8d --- /dev/null +++ b/VENDORS/Milesight/WS101/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017510FF2E01" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS101/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS101/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..e34e8ec0 --- /dev/null +++ b/VENDORS/Milesight/WS101/LORIOT/uplink/result_1.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS101", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 16, + "press": "short" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/HTTP/uplink/converter.json b/VENDORS/Milesight/WS201/HTTP/uplink/converter.json new file mode 100644 index 00000000..2048596e --- /dev/null +++ b/VENDORS/Milesight/WS201/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS201", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS201 \" + data.devEUI;\nvar deviceType = \"WS201\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DISTANCE\n else if (channel_id === 0x03 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // REMAINING AMOUNT\n else if (channel_id === 0x04 && channel_type === 0xd6) {\n decoded.remaining = input[i];\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS201/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS201/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/HTTP/uplink/payload.json b/VENDORS/Milesight/WS201/HTTP/uplink/payload.json new file mode 100644 index 00000000..1ef85a50 --- /dev/null +++ b/VENDORS/Milesight/WS201/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkA4I+AATWRQ==" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/HTTP/uplink/result.json b/VENDORS/Milesight/WS201/HTTP/uplink/result.json new file mode 100644 index 00000000..d64fa54a --- /dev/null +++ b/VENDORS/Milesight/WS201/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "WS201 24e1641092176759", + "deviceType": "WS201", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "distance": 62, + "remaining": 69 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS201/LORIOT/uplink/converter.json index e57b314c..b756da9a 100644 --- a/VENDORS/Milesight/WS201/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS201/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS201", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735213277304 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS201\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DISTANCE\n else if (channel_id === 0x03 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // REMAINING AMOUNT\n else if (channel_id === 0x04 && channel_type === 0xd6) {\n decoded.remaining = input[i];\n i += 1;\n }\n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS201\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DISTANCE\n else if (channel_id === 0x03 && channel_type === 0x82) {\n decoded.distance = parseBytesToInt(input, i, 2, false);\n i += 2;\n }\n // REMAINING AMOUNT\n else if (channel_id === 0x04 && channel_type === 0xd6) {\n decoded.remaining = input[i];\n i += 1;\n }\n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS201/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS201/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..59e6877b --- /dev/null +++ b/VENDORS/Milesight/WS201/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01756403823E0004D645" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS201/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS201/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..a7ead7a8 --- /dev/null +++ b/VENDORS/Milesight/WS201/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS201", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "distance": 62, + "remaining": 69 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/HTTP/uplink/converter.json b/VENDORS/Milesight/WS202/HTTP/uplink/converter.json new file mode 100644 index 00000000..7a47fb19 --- /dev/null +++ b/VENDORS/Milesight/WS202/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS202", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS202 \" + data.devEUI;\nvar deviceType = \"WS202\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PIR\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.pir = input[i] === 0 ? \"normal\" : \"trigger\";\n i += 1;\n }\n // DAYLIGHT\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.daylight = input[i] === 0 ? \"dark\" : \"light\";\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS202/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS202/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/HTTP/uplink/payload.json b/VENDORS/Milesight/WS202/HTTP/uplink/payload.json new file mode 100644 index 00000000..94204df7 --- /dev/null +++ b/VENDORS/Milesight/WS202/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXUQAwABBAAA" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/HTTP/uplink/result.json b/VENDORS/Milesight/WS202/HTTP/uplink/result.json new file mode 100644 index 00000000..d6d72435 --- /dev/null +++ b/VENDORS/Milesight/WS202/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "WS202 24e1641092176759", + "deviceType": "WS202", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 16, + "pir": "trigger", + "daylight": "dark" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS202/LORIOT/uplink/converter.json index 3b2665cf..a1438681 100644 --- a/VENDORS/Milesight/WS202/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS202/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS202", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735213802446 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS202\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PIR\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.pir = input[i] === 0 ? \"normal\" : \"trigger\";\n i += 1;\n }\n // DAYLIGHT\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.daylight = input[i] === 0 ? \"dark\" : \"light\";\n i += 1;\n }\n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS202\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // PIR\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.pir = input[i] === 0 ? \"normal\" : \"trigger\";\n i += 1;\n }\n // DAYLIGHT\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.daylight = input[i] === 0 ? \"dark\" : \"light\";\n i += 1;\n }\n }\n \n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS202/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS202/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..91564ce7 --- /dev/null +++ b/VENDORS/Milesight/WS202/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017510030001040000" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS202/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS202/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..d1573ad4 --- /dev/null +++ b/VENDORS/Milesight/WS202/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS202", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 16, + "pir": "trigger", + "daylight": "dark" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/HTTP/uplink/converter.json b/VENDORS/Milesight/WS301/HTTP/uplink/converter.json new file mode 100644 index 00000000..0c95323f --- /dev/null +++ b/VENDORS/Milesight/WS301/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS301", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS301 \" + data.devEUI;\nvar deviceType = \"WS301\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DOOR / WINDOW STATE (0: close 1: open)\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.magnet_status = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // INSTALL STATE (0: install 1: uninstall)\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"installed\" : \"uninstalled\";\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS301/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS301/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/HTTP/uplink/payload.json b/VENDORS/Milesight/WS301/HTTP/uplink/payload.json new file mode 100644 index 00000000..9c95766b --- /dev/null +++ b/VENDORS/Milesight/WS301/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkAwABBAAB" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/HTTP/uplink/result.json b/VENDORS/Milesight/WS301/HTTP/uplink/result.json new file mode 100644 index 00000000..7e7c7eb2 --- /dev/null +++ b/VENDORS/Milesight/WS301/HTTP/uplink/result.json @@ -0,0 +1,20 @@ +{ + "deviceName": "WS301 24e1641092176759", + "deviceType": "WS301", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "magnet_status": "open", + "tamper_status": "uninstalled" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS301/LORIOT/uplink/converter.json index e1567fcf..69ab1869 100644 --- a/VENDORS/Milesight/WS301/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS301/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS301", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735214015703 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS301\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DOOR / WINDOW STATE (0: close 1: open)\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.magnet_status = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // INSTALL STATE (0: install 1: uninstall)\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"installed\" : \"uninstalled\";\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS301\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // DOOR / WINDOW STATE (0: close 1: open)\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.magnet_status = input[i] === 0 ? \"close\" : \"open\";\n i += 1;\n }\n // INSTALL STATE (0: install 1: uninstall)\n else if (channel_id === 0x04 && channel_type === 0x00) {\n decoded.tamper_status = input[i] === 0 ? \"installed\" : \"uninstalled\";\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction setPressState(decoded, type) {\n switch (type) {\n case 1:\n decoded.press = \"short\";\n break;\n case 2:\n decoded.press = \"long\";\n break;\n case 3:\n decoded.press = \"double\";\n break;\n default:\n decoded.press = \"unknown\";\n break;\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS301/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS301/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..8969d29f --- /dev/null +++ b/VENDORS/Milesight/WS301/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017564030001040001" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS301/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS301/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..61bde284 --- /dev/null +++ b/VENDORS/Milesight/WS301/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS301", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "magnet_status": "open", + "tamper_status": "uninstalled" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/HTTP/uplink/converter.json b/VENDORS/Milesight/WS302/HTTP/uplink/converter.json new file mode 100644 index 00000000..feb24d4d --- /dev/null +++ b/VENDORS/Milesight/WS302/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS302", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS302 \" + data.devEUI;\nvar deviceType = \"WS302\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // SOUND\n else if (channel_id === 0x05 && channel_type === 0x5b) {\n var weight = input[i];\n var freq_weight = readFrequencyWeightType(weight & 0x03);\n var time_weight = readTimeWeightType((weight >> 2) & 0x03);\n \n var sound_level_name = \"L\" + freq_weight + time_weight;\n sound_level_eq_name = \"L\" + freq_weight + \"eq\";\n sound_level_max_name = \"L\" + freq_weight + time_weight + \"max\";\n decoded[sound_level_name] = parseBytesToInt(input, i + 1, 2, false) / 10;\n decoded[sound_level_eq_name] = parseBytesToInt(input, i + 3, 2, false) / 10;\n decoded[sound_level_max_name] = parseBytesToInt(input, i + 5, 2, false) / 10;\n i += 7;\n }\n // LoRaWAN Class Type\n else if (channel_id === 0xff && channel_type === 0x0f) {\n output.attributes.lorawan_class = readLoRaWANClass(input[i]);\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readFrequencyWeightType(type) {\n switch (type) {\n case 0:\n return \"Z\";\n case 1:\n return \"A\";\n case 2:\n return \"C\";\n }\n}\n\nfunction readTimeWeightType(type) {\n switch (type) {\n case 0: // impulse time weighting\n return \"I\";\n case 1: // fast time weighting\n return \"F\";\n case 2: // slow time weighting\n return \"S\";\n }\n}\n\nfunction readLoRaWANClass(type) {\n switch (type) {\n case 0:\n return \"ClassA\";\n case 1:\n return \"ClassB\";\n case 2:\n return \"ClassC\";\n case 3:\n return \"ClassCtoB\";\n }\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS302/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS302/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/HTTP/uplink/payload.json b/VENDORS/Milesight/WS302/HTTP/uplink/payload.json new file mode 100644 index 00000000..fcbd5ee3 --- /dev/null +++ b/VENDORS/Milesight/WS302/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkBVsFPwLaAWoC" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/HTTP/uplink/result.json b/VENDORS/Milesight/WS302/HTTP/uplink/result.json new file mode 100644 index 00000000..3ee8020b --- /dev/null +++ b/VENDORS/Milesight/WS302/HTTP/uplink/result.json @@ -0,0 +1,21 @@ +{ + "deviceName": "WS302 24e1641092176759", + "deviceType": "WS302", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "LAF": 57.5, + "LAeq": 47.4, + "LAFmax": 61.8 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS302/LORIOT/uplink/converter.json index 9cb5591a..f212f6b3 100644 --- a/VENDORS/Milesight/WS302/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS302/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS302", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735214125805 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS302\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // SOUND\n else if (channel_id === 0x05 && channel_type === 0x5b) {\n var weight = input[i];\n var freq_weight = readFrequencyWeightType(weight & 0x03);\n var time_weight = readTimeWeightType((weight >> 2) & 0x03);\n\n var sound_level_name = \"L\" + freq_weight + time_weight;\n var sound_level_eq_name = \"L\" + freq_weight + \"eq\";\n var sound_level_max_name = \"L\" + freq_weight + time_weight + \"max\";\n decoded[sound_level_name] = parseBytesToInt(input, i + 1, 2, false) / 10;\n decoded[sound_level_eq_name] = parseBytesToInt(input, i + 3, 2, false) / 10;\n decoded[sound_level_max_name] = parseBytesToInt(input, i + 5, 2, false) / 10;\n i += 7;\n }\n // LoRaWAN Class Type\n else if (channel_id === 0xff && channel_type === 0x0f) {\n decoded.lorawan_class = readLoRaWANClass(input[i]);\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction readFrequencyWeightType(type) {\n switch (type) {\n case 0:\n return \"Z\";\n case 1:\n return \"A\";\n case 2:\n return \"C\";\n }\n}\n\nfunction readTimeWeightType(type) {\n switch (type) {\n case 0: // impulse time weighting\n return \"I\";\n case 1: // fast time weighting\n return \"F\";\n case 2: // slow time weighting\n return \"S\";\n }\n}\n\nfunction readLoRaWANClass(type) {\n switch (type) {\n case 0:\n return \"ClassA\";\n case 1:\n return \"ClassB\";\n case 2:\n return \"ClassC\";\n case 3:\n return \"ClassCtoB\";\n }\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS302\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // SOUND\n else if (channel_id === 0x05 && channel_type === 0x5b) {\n var weight = input[i];\n var freq_weight = readFrequencyWeightType(weight & 0x03);\n var time_weight = readTimeWeightType((weight >> 2) & 0x03);\n\n var sound_level_name = \"L\" + freq_weight + time_weight;\n var sound_level_eq_name = \"L\" + freq_weight + \"eq\";\n var sound_level_max_name = \"L\" + freq_weight + time_weight + \"max\";\n decoded[sound_level_name] = parseBytesToInt(input, i + 1, 2, false) / 10;\n decoded[sound_level_eq_name] = parseBytesToInt(input, i + 3, 2, false) / 10;\n decoded[sound_level_max_name] = parseBytesToInt(input, i + 5, 2, false) / 10;\n i += 7;\n }\n // LoRaWAN Class Type\n else if (channel_id === 0xff && channel_type === 0x0f) {\n decoded.lorawan_class = readLoRaWANClass(input[i]);\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction readFrequencyWeightType(type) {\n switch (type) {\n case 0:\n return \"Z\";\n case 1:\n return \"A\";\n case 2:\n return \"C\";\n }\n}\n\nfunction readTimeWeightType(type) {\n switch (type) {\n case 0: // impulse time weighting\n return \"I\";\n case 1: // fast time weighting\n return \"F\";\n case 2: // slow time weighting\n return \"S\";\n }\n}\n\nfunction readLoRaWANClass(type) {\n switch (type) {\n case 0:\n return \"ClassA\";\n case 1:\n return \"ClassB\";\n case 2:\n return \"ClassC\";\n case 3:\n return \"ClassCtoB\";\n }\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS302/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS302/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..5e70320c --- /dev/null +++ b/VENDORS/Milesight/WS302/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017564055B053F02DA016A02" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS302/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS302/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..21ec4ed5 --- /dev/null +++ b/VENDORS/Milesight/WS302/LORIOT/uplink/result_1.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS302", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "LAF": 57.5, + "LAeq": 47.4, + "LAFmax": 61.8 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/HTTP/uplink/converter.json b/VENDORS/Milesight/WS303/HTTP/uplink/converter.json new file mode 100644 index 00000000..a85ef7d7 --- /dev/null +++ b/VENDORS/Milesight/WS303/HTTP/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "HTTP Uplink integration for WS303", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735560006385 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = \"WS303 \" + data.devEUI;\nvar deviceType = \"WS303\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 85) {\n for (var i = 0; i < input.length - 2;) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n \n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // WATER LEAK\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.leakage_status = input[i] === 0 ? \"normal\" : \"leak\";\n i += 1;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.?devEui;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.?applicationId;\nattributes.applicationName = data.?applicationName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?dataRate.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?dataRate.?spreadFactor;\nattributes.codeRate = data.txInfo.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.?loRaSNR;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n dateString = dateString.replaceFirst(\"(\\\\d{4}-\\\\d{2})(\\\\d{2})\", \"$1-$2\");\n dateString = dateString.replaceFirst(\"\\\\.(\\\\d{3})\\\\d+Z\", \".$1Z\");\n dateString = dateString.replace(\"Z\", \"+0000\");\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "applicationName", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/HTTP/uplink/metadata.json b/VENDORS/Milesight/WS303/HTTP/uplink/metadata.json new file mode 100644 index 00000000..87ee6615 --- /dev/null +++ b/VENDORS/Milesight/WS303/HTTP/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "HTTP integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/HTTP/uplink/payload.json b/VENDORS/Milesight/WS303/HTTP/uplink/payload.json new file mode 100644 index 00000000..8c94d4ab --- /dev/null +++ b/VENDORS/Milesight/WS303/HTTP/uplink/payload.json @@ -0,0 +1,31 @@ +{ + "applicationID": 1, + "applicationName": "cloud", + "deviceName": "24e1641092176759", + "devEUI": "24e1641092176759", + "time": "2020-0327T12:39:05.547336Z", + "rxInfo": [ + { + "mac": "24e124fffef021be", + "rssi": -57, + "loRaSNR": 10, + "name": "local_gateway", + "latitude": 0, + "longitude": 0, + "altitude": 0 + } + ], + "txInfo": { + "frequency": 868300000, + "dataRate": { + "modulation": "LORA", + "bandwidth": 125, + "spreadFactor": 7 + }, + "adr": false, + "codeRate": "4/5" + }, + "fCnt": 0, + "fPort": 85, + "data": "AXVkAwAA" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/HTTP/uplink/result.json b/VENDORS/Milesight/WS303/HTTP/uplink/result.json new file mode 100644 index 00000000..6d48044d --- /dev/null +++ b/VENDORS/Milesight/WS303/HTTP/uplink/result.json @@ -0,0 +1,19 @@ +{ + "deviceName": "WS303 24e1641092176759", + "deviceType": "WS303", + "attributes": { + "fPort": 85, + "applicationName": "cloud", + "frequency": 868300000, + "bandwidth": 125, + "spreadingFactor": 7, + "codeRate": "4/5" + }, + "telemetry": [{ + "ts": 1585312745547, + "values": { + "battery": 100, + "leakage_status": "normal" + } + }] +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/LORIOT/uplink/converter.json b/VENDORS/Milesight/WS303/LORIOT/uplink/converter.json index 582d7b74..1eb6d560 100644 --- a/VENDORS/Milesight/WS303/LORIOT/uplink/converter.json +++ b/VENDORS/Milesight/WS303/LORIOT/uplink/converter.json @@ -1,11 +1,16 @@ { "name": "Loriot Uplink Decoder for WS303", "type": "UPLINK", - "debugMode": true, + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1735214286964 + }, "configuration": { "scriptLang": "TBEL", "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS303\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // WATER LEAK\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.leakage_status = input[i] === 0 ? \"normal\" : \"leak\";\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size > 1) {\n telemetry = addDataToMultipleTelemetries(telemetry, addDataToTelemetry);\n }\n else if (telemetry.size == 1) {\n telemetry = addDataToSingleTelemetry(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToMultipleTelemetries(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n if (!telemetry[1][\"values\"].keys.contains(element.key)) {\n telemetry[1][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}\n\nfunction addDataToSingleTelemetry(telemetry, addDataToTelemetry) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[0][\"values\"].keys.contains(element.key)) {\n telemetry[0][\"values\"][element.key] = element.value;\n }\n }\n \n return telemetry;\n}", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"WS303\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n for (var i = 0; i < input.length - 2; ) {\n var channel_id = input[i++] & 0xff;\n var channel_type = input[i++] & 0xff;\n\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n decoded.battery = input[i];\n i += 1;\n }\n // WATER LEAK\n else if (channel_id === 0x03 && channel_type === 0x00) {\n decoded.leakage_status = input[i] === 0 ? \"normal\" : \"leak\";\n i += 1;\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ diff --git a/VENDORS/Milesight/WS303/LORIOT/uplink/payload_1.json b/VENDORS/Milesight/WS303/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..92555d08 --- /dev/null +++ b/VENDORS/Milesight/WS303/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017564030000" +} \ No newline at end of file diff --git a/VENDORS/Milesight/WS303/LORIOT/uplink/result_1.json b/VENDORS/Milesight/WS303/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..8cd89f48 --- /dev/null +++ b/VENDORS/Milesight/WS303/LORIOT/uplink/result_1.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "WS303", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery": 100, + "leakage_status": "normal" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Milesight/logo.svg b/VENDORS/Milesight/logo.svg index 07ce8092..c0abf479 100644 --- a/VENDORS/Milesight/logo.svg +++ b/VENDORS/Milesight/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/Netvox/R311A/LORIOT/uplink/payload_3.json b/VENDORS/Netvox/R311A/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..49a8a8da --- /dev/null +++ b/VENDORS/Netvox/R311A/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0102011c01000000000000" +} diff --git a/VENDORS/Netvox/R311A/LORIOT/uplink/result_3.json b/VENDORS/Netvox/R311A/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..9c07d6b9 --- /dev/null +++ b/VENDORS/Netvox/R311A/LORIOT/uplink/result_3.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R311A", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 2.8, + "contact_switch": "on" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R716S/LORIOT/uplink/payload_3.json b/VENDORS/Netvox/R716S/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..49347517 --- /dev/null +++ b/VENDORS/Netvox/R716S/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 20, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "810100000000" +} diff --git a/VENDORS/Netvox/R716S/LORIOT/uplink/result_3.json b/VENDORS/Netvox/R716S/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..9cee9f8e --- /dev/null +++ b/VENDORS/Netvox/R716S/LORIOT/uplink/result_3.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R716S", + "attributes": { + "eui": "0102030405060708", + "fPort": 20, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "cmd": "setNetvoxLoRaWANRejoinRsp", + "status": "failure" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718A/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R718A/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..ff21fdb5 --- /dev/null +++ b/VENDORS/Netvox/R718A/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "010B012406701A9E000000" +} diff --git a/VENDORS/Netvox/R718A/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R718A/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..52293a9f --- /dev/null +++ b/VENDORS/Netvox/R718A/LORIOT/uplink/result_2.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718A", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "temperature": 16.48, + "humidity": 68.14 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718AB/LORIOT/uplink/payload_3.json b/VENDORS/Netvox/R718AB/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..b2dfdd8e --- /dev/null +++ b/VENDORS/Netvox/R718AB/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0113012406701a9e000000" +} diff --git a/VENDORS/Netvox/R718AB/LORIOT/uplink/result_3.json b/VENDORS/Netvox/R718AB/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..4762d020 --- /dev/null +++ b/VENDORS/Netvox/R718AB/LORIOT/uplink/result_3.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718AB", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "temperature": 16.48, + "humidity": 68.14 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718DA/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R718DA/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..dc1d155f --- /dev/null +++ b/VENDORS/Netvox/R718DA/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "011A012401000000000000" +} diff --git a/VENDORS/Netvox/R718DA/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R718DA/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..d6d276c7 --- /dev/null +++ b/VENDORS/Netvox/R718DA/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718DA", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "ball_status": "on" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718DA2/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R718DA2/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..344f17ae --- /dev/null +++ b/VENDORS/Netvox/R718DA2/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "012F012401000000000000" +} diff --git a/VENDORS/Netvox/R718DA2/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R718DA2/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..3010e363 --- /dev/null +++ b/VENDORS/Netvox/R718DA2/LORIOT/uplink/result_2.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718DA2", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "ball_status_1": "on", + "ball_status_2": "off" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718H/LORIOT/uplink/payload_3.json b/VENDORS/Netvox/R718H/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..c77d4aab --- /dev/null +++ b/VENDORS/Netvox/R718H/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "011F012400C80000000000" +} diff --git a/VENDORS/Netvox/R718H/LORIOT/uplink/result_3.json b/VENDORS/Netvox/R718H/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..2601e313 --- /dev/null +++ b/VENDORS/Netvox/R718H/LORIOT/uplink/result_3.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718H", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "pulseCount": 200 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718KA/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R718KA/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..95332e15 --- /dev/null +++ b/VENDORS/Netvox/R718KA/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01220123074E0000000000" +} diff --git a/VENDORS/Netvox/R718KA/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R718KA/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..a6e3eee9 --- /dev/null +++ b/VENDORS/Netvox/R718KA/LORIOT/uplink/result_2.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718KA", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.5, + "current": 7, + "fineCurrent": 7.8 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R718WB/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R718WB/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..7680b019 --- /dev/null +++ b/VENDORS/Netvox/R718WB/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0112012401000000000000" +} diff --git a/VENDORS/Netvox/R718WB/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R718WB/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..2bee20a7 --- /dev/null +++ b/VENDORS/Netvox/R718WB/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R718WB", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "water_leak": "leak" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R719A/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R719A/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..170a695f --- /dev/null +++ b/VENDORS/Netvox/R719A/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0159012401000000000000" +} diff --git a/VENDORS/Netvox/R719A/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R719A/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..5ef5cf00 --- /dev/null +++ b/VENDORS/Netvox/R719A/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R719A", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 3.6, + "occupied": "on" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/R72624/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/R72624/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..08c4b728 --- /dev/null +++ b/VENDORS/Netvox/R72624/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01090778FFFFFFFF025800" +} diff --git a/VENDORS/Netvox/R72624/LORIOT/uplink/result_2.json b/VENDORS/Netvox/R72624/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..b318288b --- /dev/null +++ b/VENDORS/Netvox/R72624/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "R72624", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_volt": 12.0, + "noise": 60.0 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/RA0715/LORIOT/uplink/payload_2.json b/VENDORS/Netvox/RA0715/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..b2987cff --- /dev/null +++ b/VENDORS/Netvox/RA0715/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "010507002134FFFFFFFF00" +} diff --git a/VENDORS/Netvox/RA0715/LORIOT/uplink/result_2.json b/VENDORS/Netvox/RA0715/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..02495699 --- /dev/null +++ b/VENDORS/Netvox/RA0715/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "RA0715", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 0.0, + "co2": 850.0 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/RA0723/LORIOT/uplink/payload_3.json b/VENDORS/Netvox/RA0723/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..d6f16642 --- /dev/null +++ b/VENDORS/Netvox/RA0723/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 6, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "01050278ffff000effff00" +} diff --git a/VENDORS/Netvox/RA0723/LORIOT/uplink/result_3.json b/VENDORS/Netvox/RA0723/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..e4bcd58f --- /dev/null +++ b/VENDORS/Netvox/RA0723/LORIOT/uplink/result_3.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "RA0723", + "attributes": { + "eui": "0102030405060708", + "fPort": 6, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "battery_voltage": 12.0, + "pm2_5": 14 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Netvox/logo.svg b/VENDORS/Netvox/logo.svg index 5a0c17cd..7efc7d79 100644 --- a/VENDORS/Netvox/logo.svg +++ b/VENDORS/Netvox/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/payload_1.json b/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..ef57e31c --- /dev/null +++ b/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 2, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "A86661" +} \ No newline at end of file diff --git a/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/result_1.json b/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..4ce5b85c --- /dev/null +++ b/VENDORS/Nwave/G4_NCC_FM/LORIOT/uplink/result_1.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "G4 Car Counter FM 0102030405060708", + "deviceType": "Parking sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 2, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "type": "heartbeat", + "hw_health_status": 40, + "battery_voltage": 2.908, + "battery_voltage_mean_24h": 2.888 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/payload_1.json b/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..5f036c57 --- /dev/null +++ b/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 2, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "A86661" +} diff --git a/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/result_1.json b/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..8e0bf8bc --- /dev/null +++ b/VENDORS/Nwave/G4_NCC_SM/LORIOT/uplink/result_1.json @@ -0,0 +1,44 @@ +[{ + "deviceName": "G4 Car Counter SM 0102030405060708", + "deviceType": "Parking sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 2, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "type": "heartbeat", + "hw_health_status": 40, + "battery_voltage": 2.908, + "battery_voltage_mean_24h": 2.888 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/payload_3.json b/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..4d2cff1d --- /dev/null +++ b/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 2, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00d3d800590b" +} diff --git a/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/result_3.json b/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..35c30477 --- /dev/null +++ b/VENDORS/Nwave/G4_NPS_FM/LORIOT/uplink/result_3.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "G4 Parking sensor FM 0102030405060708", + "deviceType": "Parking sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 2, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "type": "heartbeat", + "occupied": false, + "hw_health_status": 0, + "temperature": -10, + "battery_voltage": 2.32, + "battery_health": "critical" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/payload_3.json b/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/payload_3.json new file mode 100644 index 00000000..4d2cff1d --- /dev/null +++ b/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/payload_3.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 2, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00d3d800590b" +} diff --git a/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/result_3.json b/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/result_3.json new file mode 100644 index 00000000..9d0aa664 --- /dev/null +++ b/VENDORS/Nwave/G4_NPS_SM/LORIOT/uplink/result_3.json @@ -0,0 +1,46 @@ +[{ + "deviceName": "G4 Parking sensor SM 0102030405060708", + "deviceType": "Parking sensor", + "attributes": { + "eui": "0102030405060708", + "fPort": 2, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "type": "heartbeat", + "occupied": false, + "hw_health_status": 0, + "temperature": -10, + "battery_voltage": 2.32, + "battery_health": "critical" + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Nwave/logo.svg b/VENDORS/Nwave/logo.svg index 4a0764b7..7b5b3689 100644 --- a/VENDORS/Nwave/logo.svg +++ b/VENDORS/Nwave/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/payload_2.json b/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..85665229 --- /dev/null +++ b/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 207, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "277f09a5160f0061ff5b03d3ee17fe5cfdadfd6900bbff60005b01a4" +} \ No newline at end of file diff --git a/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/result_2.json b/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..2335d742 --- /dev/null +++ b/VENDORS/SensiEDGE/SensiLora2_0/LORIOT/uplink/result_2.json @@ -0,0 +1,54 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "SensiLora2.0", + "attributes": { + "eui": "0102030405060708", + "fPort": 207, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "pressure": 1011.1, + "temperature": 24.69, + "humidity": 56.47, + "accelerationX": 0.9512450499999999, + "accelerationY": -1.61809725, + "accelerationZ": 9.60071035, + "gyroX": -0.080022005, + "gyroY": -0.00733026, + "gyroZ": -0.010384534999999999, + "magX": -66.3, + "magY": 18.7, + "magZ": -16.0, + "light": 91, + "batteryVolt": 4.2 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/SensiEDGE/logo.svg b/VENDORS/SensiEDGE/logo.svg index 39479f1d..22c4acbc 100644 --- a/VENDORS/SensiEDGE/logo.svg +++ b/VENDORS/SensiEDGE/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/VENDORS/Tektelic/Aura/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Aura/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..3601cc1d --- /dev/null +++ b/VENDORS/Tektelic/Aura/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Aura", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Aura\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Aura/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Aura/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..1ac72155 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "AP4AAVGAAABqUAAAAA==", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Aura/ChirpStack/uplink/result.json new file mode 100644 index 00000000..d723b7d5 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ChirpStack/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Aura", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/converter.json new file mode 100644 index 00000000..7de9b7ad --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Aura", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Aura\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/payload.json new file mode 100644 index 00000000..0b10cef0 --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "00FE0001518000006A50000000" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..e8ca0eaa --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00FE0001518000006A50000000" +} diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/result.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/result.json new file mode 100644 index 00000000..c0fbf70e --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/result.json @@ -0,0 +1,17 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Aura", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Aura/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..4a82f118 --- /dev/null +++ b/VENDORS/Tektelic/Aura/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Aura", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..c5ea9ae3 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Aura", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Aura\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..9d86cc7d --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "AP4AAVGAAABqUAAAAA==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..c03cf49a --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Aura", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..48f16f1e --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Aura", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Aura\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..31db41d3 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "AP4AAVGAAABqUAAAAA==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..6c5224e2 --- /dev/null +++ b/VENDORS/Tektelic/Aura/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Aura", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/info.json b/VENDORS/Tektelic/Aura/info.json new file mode 100644 index 00000000..94656046 --- /dev/null +++ b/VENDORS/Tektelic/Aura/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/aura-smart-ac-switch/", + "label": "Aura: Enhanced energy monitoring and remote control for smart homes, offices and facilities", + "description": "AURA is a 120V Smart Switch with the form factor of a standard AC Decorator-style rocker switch, incorporating a LoRa radio with power control and monitoring circuitry. It provides features such as power on-off control and status, as well as precise measurement of consumed energy (kWh), line voltage (Vrms), load current (Arms), load power (real, reactive, and apparent, W), and load power factor. AURA is a Class C device that enables real-time control and telemetry inquiry." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Aura/photo.png b/VENDORS/Tektelic/Aura/photo.png new file mode 100644 index 00000000..e43d3a0c Binary files /dev/null and b/VENDORS/Tektelic/Aura/photo.png differ diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..a228a916 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Clover", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Clover\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..9663c8dd --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "ANNaAL0KCg==", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload_1.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload_1.json new file mode 100644 index 00000000..384793b8 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/payload_1.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "AQQFeQICAtU=", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/result.json new file mode 100644 index 00000000..293d7f0f --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/result.json @@ -0,0 +1,26 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ChirpStack/uplink/result_1.json b/VENDORS/Tektelic/Clover/ChirpStack/uplink/result_1.json new file mode 100644 index 00000000..252d0cbd --- /dev/null +++ b/VENDORS/Tektelic/Clover/ChirpStack/uplink/result_1.json @@ -0,0 +1,28 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/converter.json new file mode 100644 index 00000000..45129d37 --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Clover", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Clover\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload.json new file mode 100644 index 00000000..9840ad99 --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "00d35a00bd0a0a" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..c93999c7 --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_1.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "01040579020202d5" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_2.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..52cedeef --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00d35a00bd0a0a" +} diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/result.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/result.json new file mode 100644 index 00000000..81ed555c --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/result.json @@ -0,0 +1,16 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..fa770668 --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/result_1.json @@ -0,0 +1,18 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/LORIOT/uplink/result_2.json b/VENDORS/Tektelic/Clover/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..b5fcd86a --- /dev/null +++ b/VENDORS/Tektelic/Clover/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Clover", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..630ede7b --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Clover", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Clover\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..be420fea --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "ANNaAL0KCg==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload_1.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload_1.json new file mode 100644 index 00000000..d7467513 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/payload_1.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "AQQFeQICAtU=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..b909f054 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result_1.json b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result_1.json new file mode 100644 index 00000000..2c720da6 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackCommunity/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..cb3c3c01 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Clover", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Clover\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..7f8b5359 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "ANNaAL0KCg==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload_1.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload_1.json new file mode 100644 index 00000000..1e860e20 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/payload_1.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "AQQFeQICAtU=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..a37143c7 --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result_1.json b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result_1.json new file mode 100644 index 00000000..413045ea --- /dev/null +++ b/VENDORS/Tektelic/Clover/ThingsStackIndustries/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Clover", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/info.json b/VENDORS/Tektelic/Clover/info.json new file mode 100644 index 00000000..c2d80a47 --- /dev/null +++ b/VENDORS/Tektelic/Clover/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/clover-agriculture-sensor/", + "label": "Clover: Agriculture sensor integrated soil and ambient environment sensor for smart agriculture deployments", + "description": "TEKTELIC CLOVER transforms precision agriculture by providing farmers real-time data on soil moisture, temperature, and ambient conditions. This allows for precise decision-making, optimizing crop treatments, and ensuring optimal growth conditions, ultimately boosting yields and minimizing resource wastage." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Clover/photo.png b/VENDORS/Tektelic/Clover/photo.png new file mode 100644 index 00000000..2db227a3 Binary files /dev/null and b/VENDORS/Tektelic/Clover/photo.png differ diff --git a/VENDORS/Tektelic/Comfort/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..d0432d74 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Comfort", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Comfort\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..8b17d0f9 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "A2cA4QRoTQC6C54=", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/result.json new file mode 100644 index 00000000..90b77baa --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ChirpStack/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Comfort", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/converter.json new file mode 100644 index 00000000..ae63ead2 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Comfort", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Comfort\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload.json new file mode 100644 index 00000000..b930d85b --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "036700E104684D00BA0B9E" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..56e86f64 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "036700E104684D00BA0B9E" +} diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/result.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/result.json new file mode 100644 index 00000000..584a1873 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/result.json @@ -0,0 +1,17 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Comfort", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Comfort/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..1fbc610a --- /dev/null +++ b/VENDORS/Tektelic/Comfort/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Comfort", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..3b3cc8fe --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Comfort", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Comfort\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..3ab1656f --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "A2cA4QRoTQC6C54=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..389179af --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Comfort", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..5dff1a9c --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Comfort", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Comfort\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..6ccf662e --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "A2cA4QRoTQC6C54=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..ea78fb66 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Comfort", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/info.json b/VENDORS/Tektelic/Comfort/info.json new file mode 100644 index 00000000..6c4489f1 --- /dev/null +++ b/VENDORS/Tektelic/Comfort/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/comfort-base-smart-room-sensor/", + "label": "Comfort: Compact and versatile smart room sensor for holistic indoor environment monitoring", + "description": "The Comfort is a versatile LoRaWAN IoT sensor in a compact form factor, ideal for monitoring and reporting temperature, humidity, light, shock and open/closed doors and windows in an indoor environment. Additional sensing features such as leak and motion detection, as well as counting pulses from an external device, are also supported with the appropriate model. " +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Comfort/photo.png b/VENDORS/Tektelic/Comfort/photo.png new file mode 100644 index 00000000..9c656897 Binary files /dev/null and b/VENDORS/Tektelic/Comfort/photo.png differ diff --git a/VENDORS/Tektelic/Flux/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Flux/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..3887d121 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Flux", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Flux\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Flux/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Flux/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..1ac72155 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "AP4AAVGAAABqUAAAAA==", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Flux/ChirpStack/uplink/result.json new file mode 100644 index 00000000..91810920 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ChirpStack/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Flux", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/converter.json new file mode 100644 index 00000000..152df400 --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Flux", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Flux\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/payload.json new file mode 100644 index 00000000..0b10cef0 --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "00FE0001518000006A50000000" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..e8ca0eaa --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00FE0001518000006A50000000" +} diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/result.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/result.json new file mode 100644 index 00000000..b2dc0c9f --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/result.json @@ -0,0 +1,17 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Flux", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Flux/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..9cb55f00 --- /dev/null +++ b/VENDORS/Tektelic/Flux/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Flux", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..388cc14b --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Flux", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Flux\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..9d86cc7d --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "AP4AAVGAAABqUAAAAA==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..54a2dd4c --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Flux", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..485c0ef2 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Flux", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Flux\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xFE) {\n decoded.energy_consumption_meter_elapsed = parseBytesToInt(input, i, 4);\n decoded.energy_consumption_meter_consumed = parseBytesToInt(input, i + 4, 4);\n i += 8;\n }\n else if(key_1 == 0x00 && key_2 == 0x00) {\n decoded.energy_consumption_meter_status = parseBytesToInt(input, i, 1);\n if(decoded.energy_consumption_meter_status != 0) {\n decoded.energy_consumption_meter_status != 1;\n }\n }\n else if(key_1 == 0x00 && key_2 == 0x74) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.voltmeter != 65535) {\n decoded.voltmeter = decoded.voltmeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x75) {\n decoded.voltmeter = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.ammeter != 65535) {\n decoded.ammeter = decoded.ammeter * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x80) {\n decoded.real_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.real_power != 65535) {\n decoded.real_power = decoded.real_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x80) {\n decoded.apparent_power = (input[i] << 8) | (input[i + 1] & 0xFF);\n if (decoded.apparent_power != 65535) {\n decoded.apparent_power = decoded.apparent_power * 10;\n }\n \n i += 2;\n }\n else if(key_1 == 0x00 && key_2 == 0x81) {\n decoded.power_factor_meter = input[i] & 0xFF;\n if (decoded.power_factor_meter != 65535) {\n decoded.power_factor_meter = decoded.power_factor_meter * 100;\n }\n \n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0x01) {\n decoded.relay_status = input[i] & 0xFF;\n \n if (decoded.relay_status != 0) {\n decoded.relay_status = 1;\n }\n \n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n output.attributes.loramac_join_mode = (input[i] >> 7) & 1;\n i += 2;\n }\n else if (key == 0x11) {\n var val = (input[i] >> 4) & 0x0F;\n \n if(val == 0x0C) {\n output.attributes.lora_class = \"Class C\";\n }\n else if(val == 0x0B) {\n output.attributes.lora_class = \"Class B\";\n }\n else if(val == 0x0A) {\n output.attributes.lora_class = \"Class F\";\n }\n else {\n output.attributes.lora_class = \"Invalid\";\n }\n\t\t\t\t\n\t\t\t\toutput.attributes.confirm_mode = (input[i] >> 8) & 1; \n\t\t\t\toutput.attributes.sync_word = (input[i] >> 9) & 1;\n\t\t\t\toutput.attributes.duty_cycle = (input[i] >> 10) & 1;\n\t\t\t\toutput.attributes.adr = (input[i] >> 11) & 1; \n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x71) {\n output.attributes.app_major_version = input[i];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 1];\n\t\t\t\toutput.attributes.app_revision = input[i + 2];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 4];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 5];\n\t\t\t\toutput.attributes.region = input[i + 7];\n\t\t\t\ti += 7;\n }\n }\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..31db41d3 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "AP4AAVGAAABqUAAAAA==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..b81c3b94 --- /dev/null +++ b/VENDORS/Tektelic/Flux/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Flux", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "energy_consumption_meter_elapsed": 86400, + "energy_consumption_meter_consumed": 27216, + "energy_consumption_meter_status": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/info.json b/VENDORS/Tektelic/Flux/info.json new file mode 100644 index 00000000..bd800f9e --- /dev/null +++ b/VENDORS/Tektelic/Flux/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/flux-smart-ac-outlet/", + "label": "Flux: Conveniently control and monitor power consumption for facility automation and improved building efficiency", + "description": "FLUX is a 120V Smart Outlet in the form of a standard NEMA 5-15R Decorator-style duplex tamper-resistant receptacle, incorporating a LoRa radio with power control and monitoring circuitry that enables remote power on-off control and status, as well as precise measurement of consumed energy (kWh), line voltage (Vrms), load current (Arms), load power (real, reactive, and apparent, W), and load power factor." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Flux/photo.png b/VENDORS/Tektelic/Flux/photo.png new file mode 100644 index 00000000..44033bfa Binary files /dev/null and b/VENDORS/Tektelic/Flux/photo.png differ diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..b2e7d65b --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Kiwi", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Kiwi\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..9663c8dd --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "ANNaAL0KCg==", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload_1.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload_1.json new file mode 100644 index 00000000..384793b8 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/payload_1.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "AQQFeQICAtU=", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result.json new file mode 100644 index 00000000..fe5d836f --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result.json @@ -0,0 +1,26 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result_1.json b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result_1.json new file mode 100644 index 00000000..aa5fb3d1 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ChirpStack/uplink/result_1.json @@ -0,0 +1,28 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/converter.json new file mode 100644 index 00000000..8cc2c484 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Kiwi", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Kiwi\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload.json new file mode 100644 index 00000000..9840ad99 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "00d35a00bd0a0a" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..c93999c7 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_1.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "01040579020202d5" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_2.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_2.json new file mode 100644 index 00000000..52cedeef --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/payload_2.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "00d35a00bd0a0a" +} diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result.json new file mode 100644 index 00000000..056eefaf --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result.json @@ -0,0 +1,16 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..9f966858 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_1.json @@ -0,0 +1,18 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_2.json b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_2.json new file mode 100644 index 00000000..63f56d36 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/LORIOT/uplink/result_2.json @@ -0,0 +1,42 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Kiwi", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..7a47c75e --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Kiwi", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Kiwi\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..be420fea --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "ANNaAL0KCg==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload_1.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload_1.json new file mode 100644 index 00000000..d7467513 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/payload_1.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "AQQFeQICAtU=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..afaeebe3 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result_1.json b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result_1.json new file mode 100644 index 00000000..e99a2a6f --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackCommunity/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..8fc769cd --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Kiwi", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Kiwi\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n var val = (input[i] >> 7) & 1; \n\n decoded.eos_alert = getAlarmStatus(val);\n var battery_bits = input[i] & 0x7F;\n decoded.battery_voltage = roundUsingMathRound(battery_bits * 0.01 + 2.5, 2);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xD3) {\n decoded.rem_batt_capacity = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if(key_1 == 0x00 && key_2 == 0xBD) {\n decoded.rem_batt_days = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x04) {\n var value = parseBytesToInt(input, i, 2);\n var frequencyMoisture = 0;\n\t\t\t\tif (value > 1399){\n\t\t\t\t\tfrequencyMoisture = \"Dry\";\n\t\t\t\t} else if (value > 1396 && value <= 1399){\n\t\t\t\t\tfrequencyMoisture = 0.1;\n\t\t\t\t} else if (value > 1391 && value <= 1396){\n\t\t\t\t\tfrequencyMoisture = 0.2;\n\t\t\t\t} else if (value > 1386 && value <= 1391){\n\t\t\t\t\tfrequencyMoisture = 0.3;\n\t\t\t\t} else if (value > 1381 && value <= 1386){\n\t\t\t\t\tfrequencyMoisture = 0.4;\n\t\t\t\t} else if (value > 1376 && value <= 1381){\n\t\t\t\t\tfrequencyMoisture = 0.5;\n\t\t\t\t} else if (value > 1371 && value <= 1376){\n\t\t\t\t\tfrequencyMoisture = 0.6;\n\t\t\t\t} else if (value > 1366 && value <= 1371){\n\t\t\t\t\tfrequencyMoisture = 0.7;\n\t\t\t\t} else if (value > 1361 && value <= 1366){\n\t\t\t\t\tfrequencyMoisture = 0.8;\n\t\t\t\t} else if (value > 1356 && value <= 1361){\n\t\t\t\t\tfrequencyMoisture = 0.9;\n\t\t\t\t} else if (value > 1351 && value <= 1356){\n\t\t\t\t\tfrequencyMoisture = 1.0;\n\t\t\t\t} else if (value > 1346 && value <= 1351){\n\t\t\t\t\tfrequencyMoisture = 1.1;\n\t\t\t\t} else if (value > 1341 && value <= 1346){\n\t\t\t\t\tfrequencyMoisture = 1.2;\n\t\t\t\t} else {\n\t\t\t\t\tfrequencyMoisture = \"Wet\";\n\t\t\t\t}\n\t\t\t\tdecoded.input1_frequency_to_moisture = frequencyMoisture;\n\t\t\t\tdecoded.input1_frequency = value;\n\t\t\t\ti += 2;\n }\n else if (key_1 == 0x02 && key_2 == 0x02) {\n var voltageValue = parseBytesToInt(input, i, 2) * 0.001;\n\t\t\t\tvar voltageTempOutput = -32.46 * Math.log(voltageValue * 1000) + 236.36;\n\t\t\t\tdecoded.input2_voltage_to_temp = roundUsingMathRound(voltageTempOutput, 1);\n\t\t\t\tdecoded.input2_voltage = voltageValue;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x03 && key2 == 0x02) {\n var input3Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input3_voltage = input3Voltage;\n\t\t\t\tdecoded.input3_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input3Voltage, 5)) + (217.4 * Math.pow(input3Voltage, 4)) + (-538.6 * Math.pow(input3Voltage, 3)) + (628.1 * Math.pow(input3Voltage, 2)) + (-378.9 * input3Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x03 && key2 == 0x67) {\n decoded.input3_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x04 && key2 == 0x02) {\n var input4Voltage = parseBytesToInt(input, i, 2) * 0.001;\n \n decoded.input4_voltage = input4Voltage;\n\t\t\t\tdecodedinput4_voltage_to_temp = roundUsingMathRound((-33.01 * Math.pow(input4Voltage, 5)) + (217.4 * Math.pow(input4Voltage, 4)) + (-538.6 * Math.pow(input4Voltage, 3)) + (628.1 * Math.pow(input4Voltage, 2)) + (-378.9 * input4Voltage) + 102.9, 1);\n \n i += 2;\n }\n else if (key1 == 0x04 && key2 == 0x67) {\n decoded.input4_temperature = parseBytesToInt(input, i, 2) * 0.1;\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x05 && key2 == 0x04) {\n var watermark1Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData1 = getWatermarkData(watermark1Frequency);\t\n\t\t\t\tdecoded.watermark1_tension = decodedWatermarkData1.watermarkTension;\n\t\t\t\tdecoded.watermark1_frequency = decodedWatermarkData1.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x06 && key2 == 0x04) {\n var watermark2Frequency = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tvar decodedWatermarkData2 = getWatermarkData(watermark2Frequency);\t\n\t\t\t\tdecoded.watermark2_tension = decodedWatermarkData2.watermarkTension;\n\t\t\t\tdecoded.watermark2_frequency = decodedWatermarkData2.watermarkFrequency;\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x65) {\n var lightIntensity = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\tdecoded.light_intensity = lightIntensity; \n\t\t\t\ti += 2;\n }\n else if (key1 == 0x09 && key2 == 0x00) {\n var lightDetected = parseBytesToInt(input, i, 1);\n\t\t\t\tdecoded.light_detected = getAlarmStatus(lightDetected);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0A && key2 == 0x71) {\n decoded.accelerationX = parseBytesToInt(input, i, 2);\n decoded.accelerationY = parseBytesToInt(input, i + 2, 2);\n decoded.accelerationZ = parseBytesToInt(input, i + 4, 2);\n }\n else if (key1 == 0x0A && key2 == 0x00) {\n var orientationAlarm = parseBytesToInt(input, i, 1);\n decoded.orientation_alarm = getAlarmStatus(orientationAlarm);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key1 == 0x0B && key2 == 0x67) {\n decoded.ambient_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0B && key2 == 0x68) {\n decoded.relative_humidity = parseBytesToInt(input, i, 1) * 0.5;\n i += 1;\n }\n else if (key1 == 0x0C && key2 == 0x67) {\n decoded.mcu_temperature = parseBytesToInt(input, i, 2) * 0.1;\n i += 2;\n }\n else if (key1 == 0x0D && key2 == 0x73) {\n decoded.RFU_2 = parseBytesToInt(input, i, 1) * 0.1;\n i += 1;\n }\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n var val = (((input[i] << 8) | input[i + 1]) >> 12) & 0xF;\n switch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class A\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 12:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Class C\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.deviceClassEnabled = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_input1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_input2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_input3 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_input4 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_watermark1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2A) {\n output.attributes.tick_per_watermark2 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2C) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2D) {\n output.attributes.tick_per_orientation_alarm = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2E) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n output.attributes.RFU_1 = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x30) {\n output.attributes.temperature_relative_humidity_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x31) {\n output.attributes.temperature_relative_humidity_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x32) {\n output.attributes.high_ambient_temp_raw = (((input[i] << 8) | input[i + 1]) >> 8) & 0xFF;\n output.attributes.low_ambient_temp_raw = ((input[i] << 8) | input[i + 1]) & 0xFF;\n \n i += 2;\n }\n else if(key == 0x33) {\n var val = input[i] & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x34) {\n output.attributes.high_rh = input[i];\n\t\t\t\toutput.attributes.low_rh = input[i + 1];\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x35) {\n var val = input[i] & 1;\n output.attributes.relative_humidity_threshold_enabled = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.input_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x37) {\n output.attributes.input_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x38) {\n output.attributes.low_input1 = parseBytesToInt(input, i, 2);\n output.attributes.high_input1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x39) {\n output.attributes.low_input2 = parseBytesToInt(input, i, 2);\n output.attributes.high_input2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.low_input3 = parseBytesToInt(input, i, 2);\n output.attributes.high_input3 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.low_input4 = parseBytesToInt(input, i, 2);\n output.attributes.high_input4 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3C) {\n output.attributes.low_watermark1 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark1 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3D) {\n output.attributes.low_watermark2 = parseBytesToInt(input, i, 2);\n output.attributes.high_watermark2 = parseBytesToInt(input, i + 2, 2);\n \n i += 4;\n }\n else if (key == 0x3F) {\n var input_status = input[i] & 0x3F;\n \n var bit0 = (input_status >> 0) & 1;\n output.attributes.input_enable_input1 = getEnableStatus(bit0);\n \n var bit1 = (input_status >> 1) & 1;\n output.attributes.input_enable_input2 = getEnableStatus(bit1);\n \n var bit2 = (input_status >> 2) & 1;\n output.attributes.input_enable_input3 = getEnableStatus(bit2);\n \n var bit3 = (input_status >> 3) & 1;\n output.attributes.input_enable_input4 = getEnableStatus(bit3);\n \n var bit4 = (input_status >> 4) & 1;\n output.attributes.input_enable_watermark1_enable = getEnableStatus(bit4);\n \n var bit5 = (input_status >> 5) & 1;\n output.attributes.input_enable_watermark2_enable = getEnableStatus(bit5);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.high_mcu_temp = input[i + 1];\n output.attributes.low_mcu_temp = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_enable = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.low_input3_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input3_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x45) {\n output.attributes.low_input4_onewire = parseBytesToInt(input, i, 2);\n output.attributes.high_input4_onewire = parseBytesToInt(input, i + 2, 2);\n i += 2;\n }\n else if (key == 0x48 ) {\n var val = input[i] & 1;\n output.attributes.ALS_interrupt_enabled = getEnableStatus(val);\n \n i += 1;\n }\n else if (key == 0x49) {\n output.attributes.ALS_upper_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4A) {\n output.attributes.ALS_lower_threshold = parseBytesToInt(input, i, 2);\n i +=2;\n }\n else if (key == 0x4B) {\n output.attributes.light_sample_period_idle = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4C) {\n output.attributes.light_sample_period_active = parseBytesToInt(input, i, 4);\n i +=4;\n }\n else if (key == 0x4D) {\n var status = input[i] & 0x03;\n \n var bit1 = status & 1;\n output.attributes.light_alarm_reported = getReportStatus(bit1);\n \n var bit2 = (status >> 1) & 1;\n output.attributes.light_intensity_reported = getReportStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x50) {\n output.attributes.orientation_alarm_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x51) {\n var status = input[i];\n\n var bit5 = (status >> 5) & 1;\n output.attributes.orientation_vector_report = getReportStatus(bit5);\n \n var bit0 = status & 1; \n output.attributes.orientation_alarm_report = getReportStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x52) {\n var status = input[i]; \n \n var bit7 = (status >> 7) & 1; \n var bit0 = status & 1; \n \n switch (bit7) {\n case 0:\n output.attributes.accelerometer_power_on = \"Off\";\n break;\n case 1:\n output.attributes.accelerometer_power_on = \"On\";\n break;\n default:\n output.attributes.accelerometer_power_on = \"Invalid\";\n }\n \n output.attributes.orientation_alarm_mode = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x61) {\n var status = input[i]; \n \n var bit1 = (status >> 1) & 1;\n output.attributes.report_capacity_enabled = getEnableStatus(bit1);\n \n var bit2 = (status >> 2) & 1;\n output.attributes.report_lifetime_enabled = getEnableStatus(bit2);\n \n i += 1;\n }\n else if (key == 0x62) {\n output.attributes.avg_current_window = parseBytesToInt(input, i, 1);\n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getWatermarkData(watermarkFrequency) {\n var decodedWatermark = {};\n \n var watermarkTension = 0;\n if (watermarkFrequency > 6430){\n\t\twatermarkTension = 0;\n\t} else if (watermarkFrequency >= 4330 && watermarkFrequency <= 6430){\n\t watermarkTension = 9.000 - (watermarkFrequency - 4330.000) * 0.004286;\n\t} else if (watermarkFrequency >= 2820 && watermarkFrequency < 4330){\n\t\twatermarkTension = 15.000 - (watermarkFrequency - 2820.000) * 0.003974;\n\t} else if (watermarkFrequency >= 1110 && watermarkFrequency < 2820){\n\t\twatermarkTension = 35.000 - (watermarkFrequency - 1110.000) * 0.01170;\n\t} else if (watermarkFrequency >= 770 && watermarkFrequency < 1110){\n\t\twatermarkTension = 55.000 - (watermarkFrequency - 770.000) * 0.05884;\n\t} else if (watermarkFrequency >= 600 && watermarkFrequency < 770){\n\t\twatermarkTension = 75.000 - (watermarkFrequency - 600.000) * 0.1176;\n\t} else if (watermarkFrequency >= 485 && watermarkFrequency < 600){\n\t\twatermarkTension = 100.000 - (watermarkFrequency - 485.000) * 0.2174;\n } else if (watermarkFrequency >= 293 && watermarkFrequency < 485){\n\t\twatermarkTension = 200.000 - (watermarkFrequency - 293.000) * 0.5208;\n\t} else {\n\t watermarkTension = 200;\n\t}\t\t\t\t\t\n\tdecodedWatermark.watermarkTension = roundUsingMathRound(watermarkTension, 0);\n\tdecodedWatermark.watermarkFrequency = watermarkFrequency;\n\t\t\t\t\n return decodedWatermark;\n}\n\nfunction roundUsingMathRound(value, places) {\n if (places >= 0) {\n var factor = Math.pow(10, places);\n return Math.round(value * factor) / factor; \n }\n \n return value;\n}\n\nfunction getAlarmStatus(bit) {\n var alarmResult = \"Invalid\";\n \n if (bit === 0) {\n alarmResult = \"No Alarm\";\n } else if (bit === 1 || bit === 255) {\n alarmResult = \"Alarm\";\n } else {\n alarmResult = \"Invalid\";\n }\n \n return alarmResult;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getReportStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit5) {\n case 0:\n reportResult = \"Ignore\";\n break;\n case 1:\n reportResult = \"Report\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..7f8b5359 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "ANNaAL0KCg==", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload_1.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload_1.json new file mode 100644 index 00000000..1e860e20 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/payload_1.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "AQQFeQICAtU=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..958d1f70 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "rem_batt_capacity": 90, + "rem_batt_days": 2570 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result_1.json b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result_1.json new file mode 100644 index 00000000..dd6e8fb6 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/ThingsStackIndustries/uplink/result_1.json @@ -0,0 +1,29 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Kiwi", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "input1_frequency_to_moisture": "Dry", + "input1_frequency": 1401, + "input2_voltage_to_temp": 22.6, + "input2_voltage": 0.725 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/info.json b/VENDORS/Tektelic/Kiwi/info.json new file mode 100644 index 00000000..7396f7f4 --- /dev/null +++ b/VENDORS/Tektelic/Kiwi/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/kiwi-iot-agriculture-sensor/", + "label": "KIWI: Versatile soil and ambient environment sensor for smart agriculture deployments", + "description": "The KIWI model of the Agricultural Sensor is a multi-purpose LoRaWAN IoT device designed for agricultural use. It supports the connection of up to four analog and two digital probe inputs, enabling remote data capture." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Kiwi/photo.png b/VENDORS/Tektelic/Kiwi/photo.png new file mode 100644 index 00000000..176826ee Binary files /dev/null and b/VENDORS/Tektelic/Kiwi/photo.png differ diff --git a/VENDORS/Tektelic/Vivid/ChirpStack/uplink/converter.json b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/converter.json new file mode 100644 index 00000000..04989e4d --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "ChirpStack Uplink Decoder for Vivid", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.devEui;\nvar deviceType = \"Vivid\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodePayload(input) {\n var output = {\n attributes: {},\n telemetry: []\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.fPort;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n\n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.time;\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(base64ToBytes(data.data));\n\n\nattributes.eui = data.deviceInfo.devEui;\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.deviceInfo.?devEui;\nattributes.devAddr = data.devAddr;\nattributes.fPort = data.fPort;\nattributes.applicationId = data.deviceInfo.?applicationId;\nattributes.applicationName = data.deviceInfo.?applicationName;\nattributes.tenantId = data.deviceInfo.?tenantId;\nattributes.tenantName = data.deviceInfo.?tenantName;\nattributes.deviceProfileId = data.deviceInfo.?deviceProfileId;\nattributes.deviceProfileName = data.deviceInfo.?deviceProfileName;\nattributes.frequency = data.txInfo.?frequency;\nattributes.bandwidth = data.txInfo.?modulation.?lora.?bandwidth;\nattributes.spreadingFactor = data.txInfo.?modulation.?lora.?spreadingFactor;\nattributes.codeRate = data.txInfo.?modulation.?lora.?codeRate;\n\nif(Boolean.parseBoolean(metadata[\"includeGatewayInfo\"])) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel;\n addDataToTelemetry.rfChain = gatewayInfo.rfChain;\n addDataToTelemetry.fCnt = data.fCnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.rxInfo;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "tenantId", + "tenantName", + "applicationId", + "applicationName", + "deviceProfileId", + "deviceProfileName", + "devAddr", + "fPort", + "frequency", + "bandwidth", + "spreadingFactor", + "codeRate", + "channel", + "rfChain", + "eui", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ChirpStack/uplink/metadata.json b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ChirpStack/uplink/payload.json b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/payload.json new file mode 100644 index 00000000..8b17d0f9 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/payload.json @@ -0,0 +1,48 @@ +{ + "deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e", + "time": "2023-05-22T07:47:05.404859+00:00", + "deviceInfo": { + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "deviceName": "Device name", + "devEui": "1000000000000001", + "tags": {} + }, + "devAddr": "20000001", + "adr": true, + "dr": 5, + "fCnt": 4, + "fPort": 10, + "confirmed": false, + "data": "A2cA4QRoTQC6C54=", + "rxInfo": [{ + "gatewayId": "6a7e111a10000000", + "uplinkId": 24022, + "time": "2023-05-22T07:47:05.404859+00:00", + "rssi": -35, + "snr": 11.5, + "channel": 2, + "rfChain": 1, + "location": {}, + "context": "EFwMtA==", + "metadata": { + "region_common_name": "EU868", + "region_config_id": "eu868" + }, + "crcStatus": "CRC_OK" + }], + "txInfo": { + "frequency": 868500000, + "modulation": { + "lora": { + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + } + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ChirpStack/uplink/result.json b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/result.json new file mode 100644 index 00000000..0cad6f60 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ChirpStack/uplink/result.json @@ -0,0 +1,27 @@ +{ + "deviceName": "Device name 1000000000000001", + "deviceType": "Vivid", + "attributes": { + "eui": "1000000000000001", + "devAddr": "20000001", + "fPort": 10, + "applicationId": "ca739e26-7b67-4f14-b69e-d568c22a5a75", + "applicationName": "Chirpstack application", + "tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242", + "tenantName": "ChirpStack", + "deviceProfileId": "605d08d4-65f5-4d2c-8a5a-3d2457662f79", + "deviceProfileName": "Chirpstack default device profile", + "frequency": 868500000, + "bandwidth": 125000, + "spreadingFactor": 7, + "codeRate": "CR_4_5" + }, + "telemetry": [{ + "ts": 1684741625404, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/converter.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/converter.json new file mode 100644 index 00000000..c32b899d --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/converter.json @@ -0,0 +1,29 @@ +{ + "name": "Loriot Uplink Decoder for Vivid", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": "// Decode an uplink message from a buffer\n// payload - array of bytes\n// metadata - key/value object\n\n/** Decoder **/\n\n// decode payload to string\nvar payloadStr = decodeToString(payload);\n\n// decode payload to JSON\n// var data = decodeToJson(payload);\n\nvar deviceName = 'Device A';\nvar deviceType = 'thermostat';\nvar customerName = 'Customer C';\nvar groupName = 'thermostat devices';\nvar manufacturer = 'Example corporation';\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// Result object with device/asset attributes/telemetry data\nvar result = {\n// Use deviceName and deviceType or assetName and assetType, but not both.\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: {\n model: 'Model A',\n serialNumber: 'SN111',\n integrationName: metadata['integrationName'],\n manufacturer: manufacturer\n },\n telemetry: {\n temperature: 42,\n humidity: 80,\n rawData: payloadStr\n }\n};\n\n/** Helper functions **/\n\nfunction decodeToString(payload) {\n return String.fromCharCode.apply(String, payload);\n}\n\nfunction decodeToJson(payload) {\n // covert payload to string.\n var str = decodeToString(payload);\n\n // parse string to JSON\n var data = JSON.parse(str);\n return data;\n}\n\nreturn result;", + "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.EUI;\nvar deviceType = \"Vivid\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": {\"telemetryKey\": \"telemetryValue\"}\n// }\n\nfunction decodePayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n var decoded = {};\n var fPort = data.port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\ntimestamp = data.ts;\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found.\n\nvar uplinkDataList = [];\n\n// Passing incoming bytes to decodePayload function, to get custom decoding\nvar customDecoding = decodePayload(hexToBytes(data.data));\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nattributes.eui = data.EUI;\nattributes.fPort = data.port;\nattributes.frequency = data.freq;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var addDataToTelemetry = {};\n addDataToTelemetry.rssi = data.rssi;\n addDataToTelemetry.seqno = data.seqno;\n addDataToTelemetry.snr = data.snr;\n addDataToTelemetry.ack = data.ack;\n addDataToTelemetry.toa = data.toa;\n addDataToTelemetry.fCnt = data.fcnt;\n \n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar deviceInfo = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry, \n};\n\naddAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName);\n\nuplinkDataList.add(deviceInfo);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\nvar gatewayGroupName = null; // If gatewayGroupName is not null - created device will be added to the entity group with such name.\n\nif (data.cmd == \"gw\") {\n foreach( gatewayInfo : data.gws ) {\n var addGatewayInfo = {};\n\n // You can add some keys manually telemetry\n addGatewayInfo.rssi = gatewayInfo.rssi;\n addGatewayInfo.snr = gatewayInfo.snr;\n // You can add some keys manually telemetry\n \n var gatewayInfoMsg = {\n deviceName: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n deviceType: gatewayDeviceType,\n telemetry: [{\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n }],\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n addAdditionalInfoForDeviceMsg(gatewayInfoMsg, customerName, gatewayGroupName);\n uplinkDataList.add(gatewayInfoMsg);\n }\n}\n\nreturn uplinkDataList;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "ack", + "eui", + "frequency", + "dr", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/metadata.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/metadata.json new file mode 100644 index 00000000..ae2ee743 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "Loriot integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload.json new file mode 100644 index 00000000..b930d85b --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload.json @@ -0,0 +1,17 @@ +{ + "cmd": "rx", + "seqno": 3040, + "EUI": "1000000000000001", + "ts": 1684478801936, + "fcnt": 2, + "port": 10, + "freq": 867500000, + "rssi": -21, + "snr": 10, + "toa": 206, + "dr": "SF9 BW125 4/5", + "ack": false, + "bat": 94, + "offline": false, + "data": "036700E104684D00BA0B9E" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload_1.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload_1.json new file mode 100644 index 00000000..56e86f64 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/payload_1.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 10, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "036700E104684D00BA0B9E" +} diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/result.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/result.json new file mode 100644 index 00000000..ca7d7f4e --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/result.json @@ -0,0 +1,17 @@ +[{ + "deviceName": "1000000000000001", + "deviceType": "Vivid", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "frequency": 867500000 + }, + "telemetry": [{ + "ts": 1684478801936, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/LORIOT/uplink/result_1.json b/VENDORS/Tektelic/Vivid/LORIOT/uplink/result_1.json new file mode 100644 index 00000000..bbe32d05 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/LORIOT/uplink/result_1.json @@ -0,0 +1,43 @@ +[{ + "deviceName": "0102030405060708", + "deviceType": "Vivid", + "attributes": { + "eui": "0102030405060708", + "fPort": 10, + "frequency": 868300000 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +}, { + "deviceName": "Gateway 1020304080706050", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260493, + "values": { + "rssi": -38, + "snr": 8.5 + } + }], + "attributes": { + "eui": "1020304080706050" + } +}, { + "deviceName": "Gateway 1020304081716151", + "deviceType": "Lora gateway", + "telemetry": [{ + "ts": 1690901260495, + "values": { + "rssi": -42, + "snr": 8.8 + } + }], + "attributes": { + "eui": "1020304081716151" + } +}] \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/converter.json b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/converter.json new file mode 100644 index 00000000..d3f28a84 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/converter.json @@ -0,0 +1,39 @@ +{ + "name": "The Things Stack Community Uplink Decoder for Vivid", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Vivid\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = {\n attributes: {}, telemetry: {}\n };\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n \n // --- Decoding code --- //\n return output;\n}\n\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n// If data is simulated or device doesn't send his own date string - we will use date from upcoming message, set by network server\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_adress = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\n\nvar gatewayInfo = getGatewayInfo();\nvar addDataToTelemetry = {};\naddDataToTelemetry.snr = gatewayInfo.snr;\naddDataToTelemetry.rssi = gatewayInfo.rssi;\naddDataToTelemetry.channel = gatewayInfo.channel_index;\naddDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\naddDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer.MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "device_id", + "join_eui", + "battery", + "eui", + "channel", + "applicationId", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/metadata.json b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/metadata.json new file mode 100644 index 00000000..0d75c374 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "The Things Stack Community integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/payload.json b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/payload.json new file mode 100644 index 00000000..3ab1656f --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/payload.json @@ -0,0 +1,54 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tts-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0S7ZJQ9MQPMVY49FT3SE07M", "gs:conn:01H03BQZ9342X3Y86DJ2P704E5", "gs:up:host:01H03BQZ99EGAM52KK1300GFKN", "gs:uplink:01H0S7ZJGS6D9TJSKJN8XNTMAV", "ns:uplink:01H0S7ZJGS9KKD4HTTPKFEMWCV", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0S7ZJGSF3M38ZRZVTM38DEC", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0S7ZJQ8R2EH5AA269AKM8DX"], + "received_at": "2023-05-19T05:33:35.848446463Z", + "uplink_message": { + "session_key_id": "AYfqmb0pc/1uRZv9xUydgQ==", + "f_port": 10, + "f_cnt": 10335, + "frm_payload": "A2cA4QRoTQC6C54=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6a7e111a10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-19T05:33:35.608982Z", + "timestamp": 3893546133, + "rssi": -35, + "channel_rssi": -35, + "snr": 13.2, + "frequency_offset": "69", + "uplink_token": "CiIKIAoUZXVpLTZhN2UxMTFhMTAwMDAwMDASCCThJP/+9k6eEJWZy8AOGgwIr5ScowYQvNbUsQIgiMy8y6jwpwE=", + "channel_index": 3, + "received_at": "2023-05-19T05:33:35.607383681Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "867100000", + "timestamp": 3893546133, + "time": "2023-05-19T05:33:35.608982Z" + }, + "received_at": "2023-05-19T05:33:35.641841782Z", + "consumed_airtime": "0.056576s", + "network_ids": { + "net_id": "000013", + "tenant_id": "ttn", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.network" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/result.json b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/result.json new file mode 100644 index 00000000..58607518 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackCommunity/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Vivid", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tts-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "ttn", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_adress": "eu1.cloud.thethings.network", + "bandwidth": 125000, + "frequency": "867100000" + }, + "telemetry": [{ + "ts": 1684474415641, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/converter.json b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/converter.json new file mode 100644 index 00000000..c9eb70ab --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/converter.json @@ -0,0 +1,40 @@ +{ + "name": "The Things Stack Industries Uplink Decoder for Vivid", + "type": "UPLINK", + "debugMode": false, + "debugSettings": { + "failuresEnabled": true, + "allEnabled": false, + "allEnabledUntil": 1733331880270 + }, + "configuration": { + "scriptLang": "TBEL", + "decoder": null, + "tbelDecoder": "var data = decodeToJson(payload);\n\nvar deviceName = data.end_device_ids.device_id;\nvar deviceType = \"Vivid\";\nvar groupName = null; // If groupName is not null - created device will be added to the entity group with such name.\nvar customerName = null; // If customerName is not null - created devices will be assigned to customer with such name. \n\n// use assetName and assetType instead of deviceName and deviceType\n// to automatically create assets instead of devices.\n// var assetName = 'Asset A';\n// var assetType = 'building';\n\n// If you want to parse incoming data somehow, you can add your code to this function.\n// input: bytes\n// expected output:\n// {\n// \"attributes\": {\"attributeKey\": \"attributeValue\"},\n// \"telemetry\": [{\"ts\": 1...1, \"values\": {\"telemetryKey\":\"telemetryValue\"}, {\"ts\": 1...2, \"values\": {\"telemetryKey\":\"telemetryValue\"}}]\n// }\n\nfunction decodeFrmPayload(input) {\n var output = { attributes: {}, telemetry: []};\n \n // --- Decoding code --- //\n var decoded = {};\n var fPort = data.uplink_message.f_port;\n if(fPort == 10) {\n for(var i = 0; i < input.length - 2; ) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x00 && key_2 == 0xBA) {\n decoded.battery_voltage = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x01 && key_2 == 0x00) {\n val = parseBytesToInt(input, i, 1);\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Present\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Magnet Absent\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.hall_effect_state = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if(key_1 == 0x08 && key_2 == 0x04) {\n decoded.hall_effect_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x0C && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Inactive\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Impact Alarm Active\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.impact_alarm = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key_1 == 0x05 && key_2 == 0x02) {\n decoded.impact_magnitude = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x07 && key_2 == 0x71) {\n decoded.accelerationX = toFixed(parseBytesToInt(input, i + 4, 2) * 0.001, 3);\n decoded.accelerationY = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n decoded.accelerationZ = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 6;\n }\n else if(key_1 == 0x0E && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Low(short-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.extconnector_state = \"High(open-circuit)\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.extconnector_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0F && key_2 == 0x04) {\n decoded.extconnector_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key_1 == 0x12 && key_2 == 0x04) {\n decoded.extconnector_total_count = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key_1 == 0x11 && key_2 == 0x02) {\n decoded.extconnector_analog = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key_1 == 0x0B && key_2 == 0x67) {\n decoded.mcu_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x03 && key_2 == 0x67) {\n decoded.ambient_temperature = toFixed(parseBytesToInt(input, i, 2) * 0.1, 1);\n i += 2;\n }\n else if(key_1 == 0x04 && key_2 == 0x68) {\n decoded.relative_humidity = toFixed(parseBytesToInt(input, i, 1) * 0.5, 1);\n i += 1;\n }\n else if(key_1 == 0x02 && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n \n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.light_detected = \"Dark\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.light_detected = \"Bright\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.light_detected = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x10 && key_2 == 0x02) {\n decoded.light_intensity = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0A && key_2 == 0x00) {\n var val = parseBytesToInt(input, i, 1);\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\tdecoded.motion_event_state = \"None\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 255:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Detected\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tdecoded.motion_event_state = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if(key_1 == 0x0D && key_2 == 0x04) {\n decoded.motion_event_count = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n }\n }\n else if(fPort == 0x05) {\n var key_1 = input[i++] & 0xff;\n var key_2 = input[i++] & 0xff;\n \n if(key_1 == 0x40 && key_2 == 0x06) {\n var val = input[i + 5];\n\t\t\tswitch (val){\n\t\t\t\tcase 1:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Push-button reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 2:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"DL command rest\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 4:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Independent watchdog reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tcase 8:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Power loss reset\";\n\t\t\t\t\tbreak;\n\t\t\t\tdefault:\n\t\t\t\t\tdecoded_data.reset_diagnostics_reset_reason = \"Invalid\";\n\t\t\t}\n\t\t\tdecoded_data.reset_diagnostics_power_loss_reset_count = input[i + 3];\n\t\t\tdecoded_data.reset_diagnostics_watchdog_reset_count = input[i + 2];\n\t\t\tdecoded_data.reset_diagnostics_dl_reset_count = input[i + 1];\n\t\t\tdecoded_data.reset_diagnostics_button_reset_count = input[i];\n }\n }\n else if (fPort == 100) {\n for(var i = 0; i < input.length -1; ) {\n var key = input[i++] & 0xff;\n \n if(key == 0x00) {\n output.attributes.eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n i += 8;\n }\n else if(key == 0x01) {\n output.attributes.app_eui = bytesToHex(java.util.Arrays.copyRange(input, i, i + 8));\n\t\t\t\ti += 8;\n }\n else if (key == 0x02) {\n output.attributes.app_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if (key == 0x03) {\n output.attributes.devAddr = bytesToHex(java.util.Arrays.copyRange(input, i, i + 4));\n i += 4;\n }\n else if(key == 0x04) {\n output.attributes.network_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x05) {\n output.attributes.app_session_key = bytesToHex(java.util.Arrays.copyRange(input, i, i + 16));\n i += 16;\n }\n else if(key == 0x10) {\n var val = (((input[i] << 8) | input[i + 1]) >> 15) & 1;\n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"ABP\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"OTAA\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.loramac_join_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x11) {\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 3) & 1;\n\t\t\t\toutput.attributes.adr = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 2) & 1;\n\t\t\t\toutput.attributes.duty_cycle = getEnableStatus(val);\n\t\t\t\t\n\t\t\t\tvar val = (((input[i] << 8) | input[i + 1]) >> 1) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Private\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Public\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sync_word = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = ((input[i] << 8) | input[i + 1]) & 1;\n\t\t\t\t\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Unconfirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.confirm_mode = \"Confirmed\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t output.attributes.confirm_mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 2;\n }\n else if (key == 0x12) {\n output.attributes.dr_number = (((input[i] << 8) | input[i + 1]) >> 8) & 0xF;\n output.attributes.tx_power_number = ((input[i] << 8) | input[i + 1]) & 0xF;\n \n i +=2;\n }\n else if (key == 0x13) {\n output.attributes.frequency = (((input[i] << 32) | (input[i + 1] << 24) | (input[i + 2] << 16) | (input[i + 3] << 8) | input[i + 4]) >> 8) & 0xFFFFFFFF;\n output.attributes.dr_number_rx2 = input[i + 4] & 0xFF;\n \n i += 5;\n }\n else if(key == 0x19) {\n output.attributes.netid_msb = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x1A) {\n output.attributes.loramac_net_id_lsb = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if (key == 0x20) {\n output.attributes.seconds_per_core_tick = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if(key == 0x21) {\n output.attributes.tick_per_battery = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x22) {\n output.attributes.tick_per_ambient_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x23) {\n output.attributes.tick_per_relative_humidity = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x24) {\n output.attributes.tick_per_reed_switch = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x25) {\n output.attributes.tick_per_light = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x26) {\n output.attributes.tick_per_accelerometer = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x27) {\n output.attributes.tick_per_mcu_temperature = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x28) {\n output.attributes.tick_per_pir = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x29) {\n output.attributes.tick_per_external_connector = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if(key == 0x2A) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.mode.rising_edge_enabled = getEnableStatus(bit0);\n output.attributes.mode.falling_edge_enabled = getEnableStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2B) {\n\t\t\t\toutput.attributes.reed_switch_count_threshold = parseBytesToInt(input, i, 2);\n\t\t\t\ti += 2;\n }\n else if(key == 0x2C) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n \n output.attributes.reed_values_to_transmit_report_state_enabled = getOnOffStatus(bit0);\n output.attributes.reed_values_to_transmit_report_count_enabled = getOnOffStatus(bit1);\n \n i += 1;\n }\n else if(key == 0x2D) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit7 = (input[i] >> 7) & 1;\n \n output.attributes.external_mode_rising_edge_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_mode_falling_edge_enabled_ex = getOnOffStatus(bit1);\n \n switch (bit7){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.mode = \"Digital\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t output.attributes.mode = \"Analog\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.mode = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n i += 1;\n }\n else if(key == 0x2E) {\n output.attributes.external_connector_count_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if(key == 0x2F) {\n var bit0 = (input[i] >> 0) & 1;\n var bit1 = (input[i] >> 1) & 1;\n var bit4 = (input[i] >> 4) & 1;\n \n output.attributes.external_values_to_transmit.report_state_enabled_ex = getOnOffStatus(bit0);\n output.attributes.external_values_to_transmit.report_count_enabled_ex = getOnOffStatus(bit1);\n output.attributes.external_values_to_transmit.count_type = bit4;\n i += 1;\n }\n else if(key == 0x30) {\n output.attributes.impact_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0x001, 3);\n i += 2;\n }\n else if(key == 0x31) {\n output.attributes.acceleration_event_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n i += 2;\n }\n else if(key == 0x32) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.values_to_transmit.report_periodic_alarm_enabled = getOnOffStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.values_to_transmit.report_periodic_magnitude_enabled = getOnOffStatus(bit1);\n \n var bit2 = (input[i] >> 2) & 1;\n output.attributes.values_to_transmit.report_periodic_vector_enabled = getOnOffStatus(bit2);\n \n var bit4 = (input[i] >> 4) & 1;\n output.attributes.values_to_transmit.report_event_magnitude_enabled = getOnOffStatus(bit4);\n \n var bit5 = (input[i] >> 5) & 1;\n output.attributes.values_to_transmit.report_event_vector_enabled = getOnOffStatus(bit5);\n \n i += 1;\n }\n else if(key == 0x33) {\n output.attributes.acceleration_impact_grace_period = parseBytesToInt(input, i, 2);\n\t\t\t\t\n\t\t\t\ti += 2;\n }\n else if (key == 0x34) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.acceleration_mode.impact_threshold_enabled = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.acceleration_threshold_enabled = getEnableStatus(bit1);\n\t\t\t\t\n\t\t\t\tvar bit4 = (input[i] >> 4) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.xaxis_enabled = getEnableStatus(bit4);\n\t\t\t\t\n\t\t\t\tvar bit5 = (input[i] >> 5) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.yaxis_enabled = getEnableStatus(bit5);\n\t\t\t\t\n\t\t\t\tvar bit6 = (input[i] >> 6) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.zaxis_enabled = getEnableStatus(bit6);\n\t\t\t\t\n\t\t\t\tvar bit7 = (input[i] >> 7) & 1;\n\t\t\t\toutput.attributes.acceleration_mode.poweron = getOnOffStatus(bit7);\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x35) {\n var val = (input[i] >> 1) & 0x03;\n\t\t\t\t\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"1 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"10 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"25 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"50 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 5:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"100 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"200 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"400 Hz\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_sample_rate = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\tvar val = (arg >> 4) & 0x03;\n\t\t\t\tswitch (val) {\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±2 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±4 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±8 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"±16 g\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.sensitivity_accelerometer_measurement_range = \"Invalid\";\n\t\t\t\t}\n\t\t\t\t\n\t\t\t\ti += 1;\n }\n else if (key == 0x36) {\n output.attributes.impact_alarm_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x37) {\n output.attributes.impact_alarm_threshold_count = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x38) {\n output.attributes.impact_alarm_threshold_period = parseBytesToInt(input, i, 2);\n \n i += 2;\n }\n else if (key == 0x39) {\n output.attributes.temperature_relative_humidity_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3A) {\n output.attributes.temperature_relative_humidity_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x3B) {\n output.attributes.temperature_threshold_low_temp_threshold = parseBytesToInt(input, i, 1);\n output.attributes.temperature_threshold_high_temp_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3C) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.ambient_temperature_threshold_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x3D) {\n output.attributes.rh_threshold.low_rh_threshold = parseBytesToInt(input, i, 1);\n output.attributes.rh_threshold.high_rh_threshold = parseBytesToInt(input, i + 1, 1);\n \n i += 2;\n }\n else if (key == 0x3E) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.relative_humidity_threshold_enabled =getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x40) {\n output.attributes.mcu_temperature_sample_period_idle = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x41) {\n output.attributes.mcu_temperature_sample_period_active = parseBytesToInt(input, i, 4);\n i += 4;\n }\n else if (key == 0x42) {\n output.attributes.mcu_temp_threshold_high_mcu_temp_threshold = input[i + 1];\n output.attributes.mcu_temp_threshold_low_mcu_temp_threshold = input[i];\n i += 4;\n }\n else if (key == 0x43) {\n var val = input[i] & 1;\n output.attributes.mcu_temperature_threshold_enabled = getEnableStatus(val);\n\n\t\t\t\ti += 2;\n }\n else if (key == 0x44) {\n output.attributes.analog_sample_period_idle = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x45) {\n output.attributes.analog_sample_period_active = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if(key == 0x46) {\n output.attributes.analog_threshold_high_analog_threshold = toFixed(parseBytesToInt(input, i + 2, 2) * 0.001, 3);\n\t\t\t\toutput.attributes.analog_threshold_low_analog_threshold = toFixed(parseBytesToInt(input, i, 2) * 0.001, 3);\n\t\t\t\ti += 4;\n }\n else if (key == 0x4A) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.analog_input_threshold_enabled = getEnableStatus(bit0);\n \n i +=1;\n }\n else if (key == 0x47) {\n output.attributes.light_sample_period = parseBytesToInt(input, i, 4);\n \n i += 4;\n }\n else if (key == 0x48 ) {\n var val = (input[i] >> 7) & 1;\n output.attributes.light_thresholds.threshold_enabled = getEnableStatus(val);\n output.attributes.light_thresholds.threshold = input[i] & 0x3F;\n \n i += 1;\n }\n else if (key == 0x49) {\n var bit0 = (input[i] >> 0) & 1;\n output.attributes.light_values_to_transmit.state_reported = getEnableStatus(bit0);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.light_values_to_transmit.intensity_reported = getEnableStatus(bit1);\n \n i +=1;\n }\n else if (key == 0x50) {\n output.attributes.pir_grace_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x51) {\n output.attributes.pir_threshold = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x52) {\n output.attributes.pir_threshold_period = parseBytesToInt(input, i, 2);\n i += 2;\n }\n else if (key == 0x53) {\n var bit7 = (input[i] >> 7) & 1;\n output.attributes.pir_mode.motion_count_reported = getEnableStatus(bit7);\n \n var bit6 = (input[i] >> pir_mode) & 1;\n output.attributes.pir_mode.motion_state_reported = getEnableStatus(bit6);\n \n var bit1 = (input[i] >> 1) & 1;\n output.attributes.pir_mode.event_transmission_enabled = getEnableStatus(bit1);\n \n var bit0 = (input[i] >> 0) & 1;\n output.attributes.pir_mode.transducer_enabled = getEnableStatus(bit0);\n \n i += 1;\n }\n else if (key == 0x54) {\n output.attributes.pir_mode.motion_count_reported = input[i + 1];\n output.attributes.pir_mode.motion_state_reported = input[i];\n \n i += 1;\n }\n else if (key == 0x6F) {\n var val = (input[i] >> 0) & 1;\n \n switch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid-write response format\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"4-byte CRC\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes_resp_to_dl_command_format = \"Invalid\";\n\t\t\t\t}\n \n i += 1;\n }\n else if (key == 0x71) {\n output.attributes.app_major_version = input[i + 6];\n\t\t\t\toutput.attributes.app_minor_version = input[i + 5];\n\t\t\t\toutput.attributes.app_revision = input[i + 4];\n\t\t\t\toutput.attributes.loramac_major_version = input[i + 3];\n\t\t\t\toutput.attributes.loramac_minor_version = input[i + 2];\n\t\t\t\toutput.attributes.loramac_revision = input[i + 1];\n\t\t\t\tvar val = input[i];\n\t\t\t\tswitch (val){\n\t\t\t\t\tcase 0:\n\t\t\t\t\t\toutput.attributes.region = \"EU868\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 1:\n\t\t\t\t\t\toutput.attributes.region = \"US915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 2:\n\t\t\t\t\t\toutput.attributes.region = \"AS923\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 3:\n\t\t\t\t\t\toutput.attributes.region = \"AU915\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 4:\n\t\t\t\t\t\toutput.attributes.region = \"IN865\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 6:\n\t\t\t\t\t\toutput.attributes.region = \"KR920\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tcase 7:\n\t\t\t\t\t\toutput.attributes.region = \"RU864\";\n\t\t\t\t\t\tbreak;\n\t\t\t\t\tdefault:\n\t\t\t\t\t\toutput.attributes.region = \"Invalid\";\n\t\t\t\t}\n\t\t\t\ti += 7;\n }\n }\n }\n else if (fPort == 101) {\n var size = input.length;\n var responses = [];\n \n var index = 0;\n while (index < size) {\n var downlinkFcnt = input[index++] & 0xFF; \n var numInvalidWrites = input[index++] & 0xFF; \n \n if (numInvalidWrites > 0) {\n var invalidRegisters = [];\n for (var i = 0; i < numInvalidWrites; i++) {\n invalidRegisters.add(String.format(\"0x%02X\", input[index + i]));\n }\n index += numInvalidWrites;\n \n responses.add(String.format(\n \"%d Invalid write command(s) from DL:%d for register(s): %s\",\n numInvalidWrites, downlinkFcnt, String.join(\", \", invalidRegisters)\n ));\n } else {\n responses.add(String.format(\"All write commands from DL:%d were successful\", downlinkFcnt));\n }\n }\n \n decoded.response = responses;\n }\n \n output.telemetry = [{\n ts: timestamp,\n values: decoded\n }];\n\n // --- Decoding code --- //\n return output;\n}\n\n// --- attributes and telemetry objects ---\nvar telemetry = [];\nvar attributes = {};\n// --- attributes and telemetry objects ---\n\n// --- Timestamp parsing\nvar dateString = data.uplink_message.received_at;\n\nif ((data.simulated != null && data.simulated) || dateString == null) {\n dateString = data.received_at;\n}\n\ntimestamp = parseDateToTimestamp(dateString);\n// --- Timestamp parsing\n\n// Message parsing\n// To avoid paths in the decoded objects we passing false value to function as \"pathInKey\" argument.\n// Warning: pathInKey can cause already found fields to be overwritten with the last value found, e.g. receive_at from uplink_message will be written receive_at in the root.\n\n// Passing incoming bytes to decodeFrmPayload function, to get custom decoding\nvar customDecoding = {};\nif (data.uplink_message.get(\"frm_payload\") != null) {\n customDecoding = decodeFrmPayload(base64ToBytes(data.uplink_message.frm_payload));\n}\n\n// Collecting data to result\nif (customDecoding.?telemetry.size() > 0) {\n if (customDecoding.telemetry instanceof java.util.ArrayList) {\n foreach(telemetryObj: customDecoding.telemetry) {\n if (telemetryObj.ts != null && telemetryObj.values != null) {\n telemetry.add(telemetryObj);\n }\n }\n } else {\n telemetry.putAll(customDecoding.telemetry);\n }\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\n// You can add some keys manually to attributes or telemetry\nvar applicationId = data.end_device_ids.?application_ids.?application_id;\nvar devAddr = data.end_device_ids.?dev_addr;\nvar spreadingFactor = data.uplink_message.?settings.?data_rate.?lora.?spreading_factor;\nvar codeRate = data.uplink_message.?settings.?data_rate.?lora.?coding_rate;\nvar tenantId = data.uplink_message.?network_ids.?tenant_id;\nattributes.eui = data.end_device_ids.dev_eui;\nattributes.fPort = data.uplink_message.f_port;\nattributes.applicationId = applicationId;\nattributes.devAddr = devAddr;\nattributes.spreadingFactor = spreadingFactor;\nattributes.codeRate = codeRate;\nattributes.tenantId = tenantId;\nattributes.device_id = data.end_device_ids.?device_id;\nattributes.join_eui = data.end_device_ids.?join_eui;\nattributes.net_id = data.uplink_message.?network_ids.?net_id;\nattributes.cluster_id = data.uplink_message.?network_ids.?cluster_id;\nattributes.cluster_address = data.uplink_message.?network_ids.?cluster_address;\nattributes.bandwidth = data.uplink_message.?settings.?data_rate.?lora.?bandwidth;\nattributes.frequency = data.uplink_message.?settings.?frequency;\n\nvar isIncludeGatewayInfo = metadata[\"includeGatewayInfo\"];\nif(isIncludeGatewayInfo == true) {\n var gatewayInfo = getGatewayInfo();\n var addDataToTelemetry = {};\n addDataToTelemetry.snr = gatewayInfo.snr;\n addDataToTelemetry.rssi = gatewayInfo.rssi;\n addDataToTelemetry.channel = gatewayInfo.channel_index;\n addDataToTelemetry.consumed_airtime = data.uplink_message.?consumed_airtime;\n addDataToTelemetry.fCnt = data.uplink_message.?f_cnt;\n\n telemetry = processTelemetryData(telemetry, addDataToTelemetry);\n}\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n // assetName: assetName,\n // assetType: assetType,\n attributes: attributes,\n telemetry: telemetry\n};\n\naddAdditionalInfoForDeviceMsg(result, customerName, groupName);\n\nreturn result;\n\nfunction addAdditionalInfoForDeviceMsg(deviceInfo, customerName, groupName) {\n if (customerName != null) {\n deviceInfo.customerName = customerName;\n }\n if (groupName != null) {\n deviceInfo.groupName = groupName;\n }\n}\n\nfunction parseDateToTimestamp(dateString) {\n var date = new Date(dateString);\n var timestamp = date.getTime();\n \n // If we cannot parse timestamp - we will use the current timestamp\n if (timestamp == -1) {\n timestamp = Date.now();\n }\n \n return timestamp;\n}\n\nfunction getGatewayInfo() {\n var gatewayList = data.uplink_message.?rx_metadata;\n var maxRssi = Integer. MIN_VALUE;\n var gatewayInfo = {};\n \n foreach (gateway : gatewayList) {\n if(gateway.rssi > maxRssi) {\n maxRssi = gateway.rssi;\n gatewayInfo = gateway;\n }\n }\n \n return gatewayInfo;\n}\n\nfunction processTelemetryData(telemetry, addDataToTelemetry) {\n if (telemetry.size >= 1) {\n telemetry = addDataToTelemetries(telemetry, addDataToTelemetry);\n }\n else {\n telemetry.add(addDataToTelemetry);\n }\n \n return telemetry;\n}\n\nfunction addDataToTelemetries(telemetries, addDataToTelemetry) {\n foreach(telemetry : telemetries) {\n foreach(element : addDataToTelemetry.entrySet()) {\n if(!telemetry[\"values\"].keys.contains(element.key)) {\n telemetry[\"values\"][element.key] = element.value;\n }\n } \n }\n \n return telemetries;\n}\n\nfunction getEnableStatus(bit) {\n var enableResult = \"Invalid\";\n \n switch (bit) {\n\t\tcase 0:\n\t\t enableResult = \"Disable\";\n\t\t\tbreak;\n\t\tcase 1:\n\t\t\tenableResult = \"Enable\";\n\t\t\tbreak;\n\t\tdefault:\n\t\t\tenableResult = \"Invalid\";\n\t}\n \n return enableResult;\n}\n\nfunction getOnOffStatus(bit) {\n var reportResult = \"Invalid\";\n \n switch (bit) {\n case 0:\n reportResult = \"Off\";\n break;\n case 1:\n reportResult = \"On\";\n break;\n default:\n reportResult = \"Invalid\";\n }\n \n return reportResult;\n}", + "encoder": null, + "tbelEncoder": null, + "updateOnlyKeys": [ + "fPort", + "bandwidth", + "frequency", + "net_id", + "cluster_id", + "cluster_address", + "tenant_address", + "device_id", + "join_eui", + "eui", + "channel", + "devAddr", + "spreadingFactor", + "codeRate", + "tenantId", + "applicationId", + "battery" + ] + }, + "additionalInfo": { + "description": "" + }, + "edgeTemplate": false +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/metadata.json b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/metadata.json new file mode 100644 index 00000000..23f54b34 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/metadata.json @@ -0,0 +1,4 @@ +{ + "integrationName": "ChirpStack integration", + "includeGatewayInfo": "false" +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/payload.json b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/payload.json new file mode 100644 index 00000000..6ccf662e --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/payload.json @@ -0,0 +1,77 @@ +{ + "end_device_ids": { + "device_id": "eui-1000000000000001", + "application_ids": { + "application_id": "application-tti-name" + }, + "dev_eui": "1000000000000001", + "join_eui": "2000000000000001", + "dev_addr": "20000001" + }, + "correlation_ids": ["as:up:01H0PZDGB1NW6NAPD815NGHPF6", "gs:conn:01H0FJRSXSYT7VKNYXJ89F95XT", "gs:up:host:01H0FJRSY3MZMGPPFBQ4FZV4T8", "gs:uplink:01H0PZDG4HHGFRTXRTXD4PFTH7", "ns:uplink:01H0PZDG4JZ3BM0K6J89EQK1J7", "rpc:/ttn.lorawan.v3.GsNs/HandleUplink:01H0PZDG4J02F85RYFPCNSNXCR", "rpc:/ttn.lorawan.v3.NsAs/HandleUplink:01H0PZDGB081PMP806BJHNHX1A"], + "received_at": "2023-05-18T08:25:26.112483370Z", + "uplink_message": { + "session_key_id": "AYfg8rhha5n+FWx0ZaAprA==", + "f_port": 10, + "f_cnt": 5017, + "frm_payload": "A2cA4QRoTQC6C54=", + "rx_metadata": [{ + "gateway_ids": { + "gateway_id": "eui-6A7E111A10000000", + "eui": "6A7E111A10000000" + }, + "time": "2023-05-18T08:25:25.885310Z", + "timestamp": 818273765, + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "CiIKIAoUZXVpLTZBN0UxMTFBMTAwMDAwMDASCCThJP/+9k6eEOW7l4YDGgwI9cGXowYQ5KPhrwMgiI2rp+jpOA=", + "channel_index": 2, + "received_at": "2023-05-18T08:25:25.869324983Z" + }, { + "gateway_ids": { + "gateway_id": "packetbroker" + }, + "packet_broker": { + "message_id": "01H0PZDG4MF9AYSMNY44MAVTDH", + "forwarder_net_id": "000013", + "forwarder_tenant_id": "ttn", + "forwarder_cluster_id": "eu1.cloud.thethings.network", + "forwarder_gateway_eui": "6A7E111A10000000", + "forwarder_gateway_id": "eui-6a7e111a10000000", + "home_network_net_id": "000013", + "home_network_tenant_id": "tenant", + "home_network_cluster_id": "eu1.cloud.thethings.industries" + }, + "time": "2023-05-18T08:25:25.885310Z", + "rssi": -24, + "channel_rssi": -24, + "snr": 12, + "frequency_offset": "671", + "uplink_token": "eyJnIjoiWlhsS2FHSkhZMmxQYVVwQ1RWUkpORkl3VGs1VE1XTnBURU5LYkdKdFRXbFBhVXBDVFZSSk5GSXdUazVKYVhkcFlWaFphVTlwU201a01uaGhWVlJvZDFSWFVuRmlSM1JtVFcxT2RVbHBkMmxrUjBadVNXcHZhV05ZY0RKT1IyeExaREpSZVZwR1pIUmpNRXBLVlVoR2RFNVZkR3BWVTBvNUxua3paVVJTWVRaM1lXOU1kbTQwVm5sdmIyWmlPWGN1ZUhCZmVrcElaa3hIWlZadGRVUlFVeTVuYlRaVlZXRXdkakpHV0VKMGJUUjZaMjVXUkVoeGVHRjRaMlJKTlVkS1VsbERhemc1VDNCbk5rVk1iM1JDUkVZM1VWbHdZbEJDTkdOblNqWjBlbkphYUV4MFRVMHhZMVZFTTFac01XdExURUo0YURaMFExTnhhMVJsWWw4eE5FdHlVVXcyZUhsRWFFbEhlakJITXpoTE0xaFdlRzR5VUVjMk4wNUViME5WTkhoTmRrazFZVk5oWkUwd2FXVnFjR294VGtoMFduZHlZMDFxVlVGNmRsbERUazlNY2s5eFdVeFpWMk5XTG1WVFFYVkpNVkptT1U5NWRqUTNhSEoxTUZoalYxRT0iLCJhIjp7ImZuaWQiOiIwMDAwMTMiLCJmdGlkIjoidHRuIiwiZmNpZCI6ImV1MS5jbG91ZC50aGV0aGluZ3MubmV0d29yayJ9fQ==", + "received_at": "2023-05-18T08:25:25.906038642Z" + }], + "settings": { + "data_rate": { + "lora": { + "bandwidth": 125000, + "spreading_factor": 7, + "coding_rate": "4/5" + } + }, + "frequency": "868500000", + "timestamp": 818273765, + "time": "2023-05-18T08:25:25.885310Z" + }, + "received_at": "2023-05-18T08:25:25.906399073Z", + "consumed_airtime": "0.097536s", + "network_ids": { + "net_id": "000013", + "tenant_id": "tenant", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "tenant_address": "tenant.eu1.cloud.thethings.industries" + } + } +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/result.json b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/result.json new file mode 100644 index 00000000..92882bcd --- /dev/null +++ b/VENDORS/Tektelic/Vivid/ThingsStackIndustries/uplink/result.json @@ -0,0 +1,28 @@ +{ + "deviceName": "eui-1000000000000001", + "deviceType": "Vivid", + "attributes": { + "eui": "1000000000000001", + "fPort": 10, + "applicationId": "application-tti-name", + "devAddr": "20000001", + "spreadingFactor": 7, + "codeRate": "4/5", + "tenantId": "tenant", + "device_id": "eui-1000000000000001", + "join_eui": "2000000000000001", + "net_id": "000013", + "cluster_id": "eu1", + "cluster_address": "eu1.cloud.thethings.industries", + "bandwidth": 125000, + "frequency": "868500000" + }, + "telemetry": [{ + "ts": 1684398325906, + "values": { + "ambient_temperature": 22.5, + "relative_humidity": 38.5, + "battery_voltage": 2.974 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/info.json b/VENDORS/Tektelic/Vivid/info.json new file mode 100644 index 00000000..ec59d281 --- /dev/null +++ b/VENDORS/Tektelic/Vivid/info.json @@ -0,0 +1,5 @@ +{ + "url": "https://tektelic.com/products/sensors/vivid-pir-smart-room-sensor/", + "label": "Vivid: Smart room & occupancy sensor", + "description": "The Vivid is a versatile LoRaWAN IoT sensor in a compact form factor, ideal for monitoring and reporting temperature, humidity, light, shock and open/closed doors and windows in an indoor environment. Additional sensing features such as leak and motion detection, as well as counting pulses from an external device, are also supported with the appropriate model." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/Vivid/photo.png b/VENDORS/Tektelic/Vivid/photo.png new file mode 100644 index 00000000..865af723 Binary files /dev/null and b/VENDORS/Tektelic/Vivid/photo.png differ diff --git a/VENDORS/Tektelic/info.json b/VENDORS/Tektelic/info.json new file mode 100644 index 00000000..e80e6456 --- /dev/null +++ b/VENDORS/Tektelic/info.json @@ -0,0 +1,4 @@ +{ + "url": "https://tektelic.com/", + "description": "TEKTELIC is a leading supplier of LoRaWAN IoT Gateways, End-to-End Solutions, Sensors, and Applications. TEKTELIC has been present in the market since 2009 and during these years, has managed to achieve excellence in quality, engineering, and service." +} \ No newline at end of file diff --git a/VENDORS/Tektelic/logo.svg b/VENDORS/Tektelic/logo.svg new file mode 100644 index 00000000..6bbde7cd --- /dev/null +++ b/VENDORS/Tektelic/logo.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/VENDORS/Yobiiq/EM2101/LORIOT/uplink/payload_5.json b/VENDORS/Yobiiq/EM2101/LORIOT/uplink/payload_5.json new file mode 100644 index 00000000..73297087 --- /dev/null +++ b/VENDORS/Yobiiq/EM2101/LORIOT/uplink/payload_5.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 50, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "FF090200FF0A2301FF1601ED0333FF0F02FF0B01FF0001FF28454D32313031" +} diff --git a/VENDORS/Yobiiq/EM2101/LORIOT/uplink/result_5.json b/VENDORS/Yobiiq/EM2101/LORIOT/uplink/result_5.json new file mode 100644 index 00000000..eec7a385 --- /dev/null +++ b/VENDORS/Yobiiq/EM2101/LORIOT/uplink/result_5.json @@ -0,0 +1,27 @@ +{ + "deviceName": "0102030405060708", + "deviceType": "Electricity Meter", + "groupName": "Electricity Meters", + "attributes": { + "codecVersion": "1.0.0", + "genericModel": "EM2101", + "productCode": "P1002009", + "manufacturer": "YOBIIQ B.V.", + "devEui": "0102030405060708", + "latestFPort": 50, + "hardwareVersion": "V2.0", + "firmwareVersion": "V23.1", + "deviceSerialNumber": 32310067, + "deviceClass": "Class C", + "powerEvent": "AC Power On", + "relayStatus": "HIGH", + "deviceModel": "EM2101" + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "fPort": 50, + "fCnt": 3 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Yobiiq/EM4301/LORIOT/uplink/payload_4.json b/VENDORS/Yobiiq/EM4301/LORIOT/uplink/payload_4.json new file mode 100644 index 00000000..6c65ff8f --- /dev/null +++ b/VENDORS/Yobiiq/EM4301/LORIOT/uplink/payload_4.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 1, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "0101673B240C0203673B21B4030403080F040405000000000506000000000607000000000B0A00" +} diff --git a/VENDORS/Yobiiq/EM4301/LORIOT/uplink/result_4.json b/VENDORS/Yobiiq/EM4301/LORIOT/uplink/result_4.json new file mode 100644 index 00000000..730e7bf3 --- /dev/null +++ b/VENDORS/Yobiiq/EM4301/LORIOT/uplink/result_4.json @@ -0,0 +1,30 @@ +{ + "deviceName": "0102030405060708", + "deviceType": "Electricity Meter", + "groupName": "Electricity Meters", + "attributes": { + "codecVersion": "1.0.0", + "genericModel": "EM4301", + "productCode": "P1002011", + "manufacturer": "YOBIIQ B.V.", + "devEui": "0102030405060708", + "latestFPort": 1 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "fPort": 1, + "fCnt": 3 + } + }, { + "ts": 1731928500000, + "values": { + "timestamp": 1731929100, + "activeEnergyImportL123T1": 50859780, + "activeEnergyImportL123T2": 0, + "activeEnergyExportL123T1": 0, + "activeEnergyExportL123T2": 0, + "modbusErrorCode": 0 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Yobiiq/RM200/LORIOT/uplink/payload_4.json b/VENDORS/Yobiiq/RM200/LORIOT/uplink/payload_4.json new file mode 100644 index 00000000..2eaabe1a --- /dev/null +++ b/VENDORS/Yobiiq/RM200/LORIOT/uplink/payload_4.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 11, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "AAFE6735D830AA0101AA9700000005AA0203AA9A00000005" +} diff --git a/VENDORS/Yobiiq/RM200/LORIOT/uplink/result_4.json b/VENDORS/Yobiiq/RM200/LORIOT/uplink/result_4.json new file mode 100644 index 00000000..98945b7d --- /dev/null +++ b/VENDORS/Yobiiq/RM200/LORIOT/uplink/result_4.json @@ -0,0 +1,34 @@ +{ + "deviceName": "0102030405060708", + "deviceType": "Digital Controller", + "groupName": "Digital Controllers", + "attributes": { + "codecVersion": "1.0.0", + "genericModel": "RM200", + "productCode": "P1002003", + "manufacturer": "YOBIIQ B.V.", + "devEui": "0102030405060708", + "latestFPort": 11 + }, + "telemetry": [{ + "ts": 1690901260493, + "values": { + "fPort": 11, + "fCnt": 3 + } + }, { + "ts": 1731582000000, + "values": { + "channel1State": { + "state": "ON", + "reason": "AUTO" + }, + "channel1Counter": 5, + "channel2State": { + "state": "ON", + "reason": "MANUAL" + }, + "channel2Counter": 5 + } + }] +} \ No newline at end of file diff --git a/VENDORS/Yobiiq/SD1001/LORIOT/uplink/payload_4.json b/VENDORS/Yobiiq/SD1001/LORIOT/uplink/payload_4.json new file mode 100644 index 00000000..5350e1b1 --- /dev/null +++ b/VENDORS/Yobiiq/SD1001/LORIOT/uplink/payload_4.json @@ -0,0 +1,32 @@ +{ + "cmd": "gw", + "seqno": 4, + "EUI": "0102030405060708", + "ts": 1690901260493, + "fcnt": 3, + "port": 8, + "freq": 868300000, + "toa": 1319, + "dr": "SF12 BW125 4/5", + "ack": false, + "gws": [ + { + "rssi": -38, + "snr": 8.5, + "ts": 1690901260493, + "time": "2023-08-01T14:47:40.493Z", + "gweui": "1020304080706050", + "ant": 0 + }, + { + "rssi": -42, + "snr": 8.8, + "ts": 1690901260495, + "time": "2023-08-01T14:47:40.495Z", + "gweui": "1020304081716151", + "ant": 0 + } + ], + "bat": 143, + "data": "017564020B00030000040000050000060000" +} diff --git a/VENDORS/Yobiiq/SD1001/LORIOT/uplink/result_4.json b/VENDORS/Yobiiq/SD1001/LORIOT/uplink/result_4.json new file mode 100644 index 00000000..6c07865b --- /dev/null +++ b/VENDORS/Yobiiq/SD1001/LORIOT/uplink/result_4.json @@ -0,0 +1,25 @@ +{ + "deviceName": "0102030405060708", + "deviceType": "Smoke Detector", + "groupName": "Smoke Detectors", + "attributes": { + "devEui": "0102030405060708", + "codecVersion": "1.1.0", + "deviceModel": "SD-1001", + "productCode": "1002015", + "manufacturer": "YOBIIQ B.V." + }, + "telemetry": { + "ts": 1690901260493, + "values": { + "fPort": 8, + "fCnt": 3, + "batteryLevelInPercentage": 100, + "powerEvent": "AC Power Off", + "lowBatteryAlarm": "Normal", + "faultAlarm": "Normal", + "smokeAlarm": "Normal", + "interconnectAlarm": "Normal" + } + } +} \ No newline at end of file diff --git a/VENDORS/Yobiiq/logo.svg b/VENDORS/Yobiiq/logo.svg index b53db240..6ff5ef95 100644 --- a/VENDORS/Yobiiq/logo.svg +++ b/VENDORS/Yobiiq/logo.svg @@ -1,9 +1,9 @@ - - + + - - + + - + diff --git a/data_converters_validator.py b/data_converters_validator.py index 8d38e136..c3967404 100644 --- a/data_converters_validator.py +++ b/data_converters_validator.py @@ -182,6 +182,38 @@ def validate_device_files(device_path): return True +def validate_company_files(company_path): + required_files = ['logo.svg', 'info.json'] + all_present = True + + for file in required_files: + file_path = os.path.join(company_path, file) + if not os.path.exists(file_path): + print(f"Validation failed: Missing '{file}' in {company_path}") + all_present = False + + if "info.json" in required_files: + info_path = os.path.join(company_path, "info.json") + if os.path.exists(info_path): + with open(info_path, 'r') as f: + try: + data = json.load(f) + except json.JSONDecodeError: + print(f"Validation failed: 'info.json' in {company_path} is not valid JSON") + return False + + allowed_keys = {"description", "url"} + if set(data.keys()) != allowed_keys: + print(f"Validation failed: 'info.json' in {company_path} contains invalid keys. Allowed keys are {allowed_keys}.") + return False + + for key in allowed_keys: + if not data[key] or not isinstance(data[key], str): + print(f"Validation failed: '{key}' in 'info.json' in {company_path} is missing or empty.") + return False + + return all_present + def walk_vendors_directory(root_dir): all_success = True @@ -191,6 +223,10 @@ def walk_vendors_directory(root_dir): if not os.path.isdir(company_path): continue + if not validate_company_files(company_path): + all_success = False + continue + for device in os.listdir(company_path): device_path = os.path.join(company_path, device)