Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WS202 #87

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 33 additions & 38 deletions VENDORS/Milesight/WS202/ChirpStack/uplink/converter.json
Original file line number Diff line number Diff line change
@@ -1,39 +1,34 @@
{
"name": "ChirpStack uplink converter for Milesight WS202",
"type": "UPLINK",
"debugMode": true,
"configuration": {
"scriptLang": "TBEL",
"decoder": null,
"tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName;\nvar deviceType = data.deviceInfo.deviceProfileName;\nvar groupName = 'IAQ devices';\n// var customerName = 'Customer A';\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//\n// In the example - bytes will be saved as HEX string and also parsed as light level, battery level and PIR sensor value.\n//\n\nfunction decodePayload(input) {\n var output = { attributes:{}, telemetry: {} };\n // --- Decoding code --- //\n\n output.telemetry.HEX_bytes = bytesToHex(input);\n\n // If the length of the input byte array is odd - we cannot parse it using the example below\n if (input.length > 0) {\n for (var i = 0; i < input.length; ) {\n var channel_id = input[i++];\n if (i < input.length) {\n var channel_type = input[i++];\n // BATTERY\n if (channel_id === 0x01 && channel_type === 0x75) {\n output.telemetry.battery = input[i];\n i += 1;\n }\n // PIR\n else if (channel_id === 0x03 && channel_type === 0x00) {\n output.telemetry.pir = input[i] === 0 ? \"normal\" : \"trigger\";\n i += 1;\n }\n // DAYLIGHT\n else if (channel_id === 0x04 && channel_type === 0x00) {\n output.telemetry.daylight = input[i] === 0 ? \"dark\" : \"light\";\n i += 1;\n }\n }\n }\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;\nvar timestamp = -1;\nif (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\nif (timestamp == -1) {\n timestamp = Date.now();\n}\n// --- Timestamp parsing\n\n// You can add some keys manually to attributes or telemetry\nattributes.deduplicationId = data.deduplicationId;\n\n// You can exclude some keys from the result\nvar excludeFromAttributesList = [\"deviceName\", \"rxInfo\", \"confirmed\", \"data\", \"deduplicationId\",\"time\", \"adr\", \"dr\", \"fCnt\"];\nvar excludeFromTelemetryList = [\"data\", \"deviceInfo\", \"txInfo\", \"devAddr\", \"adr\", \"time\", \"fPort\", \"region_common_name\", \"region_config_id\", \"deduplicationId\"];\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 telemetryData = toFlatMap(data, excludeFromTelemetryList, false);\nvar attributesData = toFlatMap(data, excludeFromAttributesList, false);\n\nvar uplinkDataList = [];\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 telemetry.putAll(customDecoding.telemetry);\n}\n\nif (customDecoding.?attributes.size() > 0) {\n attributes.putAll(customDecoding.attributes);\n}\n\ntelemetry.putAll(telemetryData);\nattributes.putAll(attributesData);\n\nvar result = {\n deviceName: deviceName,\n deviceType: deviceType,\n// assetName: assetName,\n// assetType: assetType,\n// customerName: customerName,\n groupName: groupName,\n attributes: attributes,\n telemetry: {\n ts: timestamp,\n values: telemetry\n }\n};\n\nreturn result;",
"encoder": null,
"tbelEncoder": null,
"updateOnlyKeys": [
"tenantId",
"tenantName",
"applicationId",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devEui",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"battery",
"pir",
"daylight",
"confirmed",
"gatewayId",
"channel",
"rfChain",
"crcStatus"
]
},
"additionalInfo": {
"description": "This converter is used to convert uplink data from WS202 device to ThingsBoard device data."
},
"edgeTemplate": false
}
"name": "ChirpStack Uplink Decoder for WS202",
"type": "UPLINK",
"debugMode": true,
"configuration": {
"scriptLang": "TBEL",
"decoder": null,
"tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName + \" \" + data.deviceInfo.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 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 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 timestamp = -1;\n if (dateString != null) {\n timestamp = new Date(dateString).getTime();\n if (timestamp == -1) {\n var secondsSeparatorIndex = dateString.lastIndexOf(\n '.') + 1;\n var millisecondsEndIndex = dateString.lastIndexOf(\n '+');\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf(\n 'Z');\n }\n if (millisecondsEndIndex == -1) {\n millisecondsEndIndex = dateString.lastIndexOf(\n '-');\n }\n if (millisecondsEndIndex == -1) {\n if (dateString.length >= secondsSeparatorIndex +\n 3) {\n dateString = dateString.substring(0,\n secondsSeparatorIndex + 3);\n }\n } else {\n dateString = dateString.substring(0,\n secondsSeparatorIndex + 3) +\n dateString.substring(millisecondsEndIndex,\n 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 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 = 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": [
"tenantId",
"tenantName",
"applicationId",
"applicationName",
"deviceProfileId",
"deviceProfileName",
"devAddr",
"fPort",
"frequency",
"bandwidth",
"spreadingFactor",
"codeRate",
"channel",
"rfChain",
"eui",
"battery"
]
},
"additionalInfo": {
"description": ""
},
"edgeTemplate": false
}
3 changes: 2 additions & 1 deletion VENDORS/Milesight/WS202/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"
}
86 changes: 43 additions & 43 deletions VENDORS/Milesight/WS202/ChirpStack/uplink/payload.json
Original file line number Diff line number Diff line change
@@ -1,48 +1,48 @@
{
"deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e",
"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": "AXUQAwABBAAA",
"rxInfo": [{
"gatewayId": "6a7e111a10000000",
"uplinkId": 24022,
"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": {}
"rssi": -35,
"snr": 11.5,
"channel": 2,
"rfChain": 1,
"location": {},
"context": "EFwMtA==",
"metadata": {
"region_common_name": "EU868",
"region_config_id": "eu868"
},
"devAddr": "20000001",
"adr": true,
"dr": 5,
"fCnt": 4,
"fPort": 85,
"confirmed": false,
"data": "AXVdAwABBAAA",
"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"
}
}
"crcStatus": "CRC_OK"
}],
"txInfo": {
"frequency": 868500000,
"modulation": {
"lora": {
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
}
}
}
}
62 changes: 24 additions & 38 deletions VENDORS/Milesight/WS202/ChirpStack/uplink/result.json
Original file line number Diff line number Diff line change
@@ -1,41 +1,27 @@
{
"deviceName": "Device name",
"deviceType": "Chirpstack default device profile",
"groupName": "IAQ devices",
"attributes": {
"deduplicationId": "57433366-50a6-4dc2-8145-2df1bbc70d9e",
"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",
"devEui": "1000000000000001",
"devAddr": "20000001",
"fPort": 85,
"frequency": 868500000,
"bandwidth": 125000,
"spreadingFactor": 7,
"codeRate": "CR_4_5"
},
"telemetry": {
"ts": 1684741625404,
"values": {
"HEX_bytes": "01755D030001040000",
"battery": 93,
"pir": "trigger",
"daylight": "dark",
"dr": 5,
"fCnt": 4,
"confirmed": false,
"gatewayId": "6a7e111a10000000",
"uplinkId": 24022,
"rssi": -35,
"snr": 11.5,
"channel": 2,
"rfChain": 1,
"context": "EFwMtA==",
"crcStatus": "CRC_OK"
}
"deviceName": "Device name 1000000000000001",
"deviceType": "WS202",
"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": 16,
"pir": "trigger",
"daylight": "dark"
}
}]
}
Loading