Skip to content

Commit

Permalink
Merge pull request #14 from artem-barysh-dev/AM308
Browse files Browse the repository at this point in the history
AM308
  • Loading branch information
ashvayka authored Oct 31, 2024
2 parents e552574 + 1567172 commit aa175fc
Show file tree
Hide file tree
Showing 29 changed files with 786 additions and 74 deletions.
70 changes: 33 additions & 37 deletions VENDORS/Milesight/AM308/ChirpStack/uplink/converter.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion VENDORS/Milesight/AM308/ChirpStack/uplink/metadata.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
{
"integrationName": "ChirpStack integration"
"integrationName": "ChirpStack integration",
"includeGatewayInfo": false
}
Empty file.
Empty file.
48 changes: 48 additions & 0 deletions VENDORS/Milesight/AM308/ChirpStack/uplink/payload_2.json
Original file line number Diff line number Diff line change
@@ -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": "AXVVA2fuAARofAUAAQbLAgd9qAMIfSUACXNmJwp9BAALfSAADH0wAA19IAAOAVUgzmU1ygwA4QBuAWQBwgAKJ5cAGQAy",
"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"
}
}
}
}
34 changes: 34 additions & 0 deletions VENDORS/Milesight/AM308/ChirpStack/uplink/result_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"deviceName": "AM308 Device name",
"deviceType": "AM308",
"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": 85,
"temperature": 23.8,
"humidity": 62.0,
"pir": "trigger",
"light_level": 2,
"co2": 936,
"tvoc": 37,
"pressure": 1008.6,
"pm2_5": 32,
"pm10": 48
}
}]
}
Original file line number Diff line number Diff line change
@@ -1,49 +1,48 @@
{
"deviceName": "Device name",
"deviceType": "Chirpstack default device profile",
"groupName": "8-in-1 IAQ Sensor",
"deviceName": "AM308 Device name",
"deviceType": "AM308",
"attributes": {
"deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e",
"tenantId": "52f14cd4-c6f1-4fbd-8f87-4025e1d49242",
"tenantName": "ChirpStack",
"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",
"devEui": "1000000000000001",
"devAddr": "20000001",
"fPort": 85,
"frequency": 868500000,
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
},
"telemetry": {
"telemetry": [{
"ts": 1684741625404,
"values": {
"hexString": "0175550367EE0004687C05000106CB02077DA803087D2500097366270A7D04000B7D20000C7D3000",
"battery": 85,
"temperature": 23.8,
"humidity": 62,
"humidity": 62.0,
"pir": "trigger",
"light_level": 2,
"co2": 936,
"tvoc": 37,
"pressure": 1008.6,
"hcho": 0.04,
"pm2_5": 32,
"pm10": 48,
"dr": 5,
"fCnt": 4,
"confirmed": false,
"gatewayId": "6a7e111a10000000",
"uplinkId": 24022,
"rssi": -35,
"snr": 11.5,
"channel": 2,
"rfChain": 1,
"context": "EFwMtA==",
"crcStatus": "CRC_OK"
"beep": "no"
}
}, {
"ts": 214578533,
"values": {
"temperature": 5760.0,
"humidity": 14080.0,
"trigger": "idle",
"light_level": 100,
"co2": 49665,
"tvoc": 2560,
"pressure": 3869.5,
"pm2_5": 6400,
"pm10": 12800
}
}
}]
}
25 changes: 25 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/converter.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "Uplink data converter for Loriot integration AM-308",
"type": "UPLINK",
"debugMode": true,
"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),\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}",
"encoder": null,
"tbelEncoder": null,
"updateOnlyKeys": [
"fPort",
"battery",
"ack",
"beep",
"eui",
"frequency",
"dr"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
4 changes: 4 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/metadata.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
{
"integrationName": "Loriot integration",
"includeGatewayInfo": false
}
17 changes: 17 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/payload_1.json
Original file line number Diff line number Diff line change
@@ -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": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d3000"
}
17 changes: 17 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/payload_2.json
Original file line number Diff line number Diff line change
@@ -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": "0175550367ee0004687c05000106cb02077da803087d2500097366270a7d04000b7d20000c7d30000d7d20000e015520ce6535ca0c00e1006e016401c2000a279700190032"
}
24 changes: 24 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/result_1.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
[{
"deviceName": "AM308 1000000000000001",
"deviceType": "AM308",
"attributes": {
"eui": "1000000000000001",
"fPort": 85,
"frequency": 867500000
},
"telemetry": [{
"ts": 1684478801936,
"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
}
}]
}]
38 changes: 38 additions & 0 deletions VENDORS/Milesight/AM308/Loriot/uplink/result_2.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
[{
"deviceName": "AM308 1000000000000001",
"deviceType": "AM308",
"attributes": {
"eui": "1000000000000001",
"fPort": 85,
"frequency": 867500000
},
"telemetry": [{
"ts": 1684478801936,
"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": 214578533,
"values": {
"temperature": 5760.0,
"humidity": 14080.0,
"trigger": "idle",
"light_level": 100,
"co2": 49665,
"tvoc": 2560,
"pressure": 3869.5,
"pm2_5": 6400,
"pm10": 12800
}
}]
}]
Loading

0 comments on commit aa175fc

Please sign in to comment.