diff --git a/VENDORS/Milesight/AM307/ChirpStack/uplink/converter.json b/VENDORS/Milesight/AM307/ChirpStack/uplink/converter.json index 810fb7a..864e8e9 100644 --- a/VENDORS/Milesight/AM307/ChirpStack/uplink/converter.json +++ b/VENDORS/Milesight/AM307/ChirpStack/uplink/converter.json @@ -5,7 +5,7 @@ "configuration": { "scriptLang": "TBEL", "decoder": null, - "tbelDecoder": "var data = decodeToJson(payload);\nvar deviceName = data.deviceInfo.deviceName;\nvar deviceType = data.deviceInfo.deviceProfileName;\nvar groupName = '7-in-1 IAQ Sensor';\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(inputArray) {\n var output = { attributes:{}, telemetry: {} };\n // --- Decoding code --- //\n var input = bytesToExecutionArrayList(inputArray);\n output.telemetry.HEX_bytes = bytesToHex(input);\n\n var historyData = {};\n var decoded = {};\n decoded.hexString = bytesToHex(input);\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 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 = input[i] / 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 (AM307)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n historyData = {};\n historyData.timestamp = parseBytesToInt(input, i, 4, false);\n historyData.temperature = parseBytesToInt(input, i + 4, 2, false) / 10;\n historyData.humidity = parseBytesToInt(input, i + 6, 2, false) / 2;\n historyData.pir = input[i + 8] === 1 ? \"trigger\" : \"idle\";\n historyData.light_level = input[i + 9] === 1;\n historyData.co2 = parseBytesToInt(input, i + 10, 2, false);\n historyData.tvoc = parseBytesToInt(input, i + 12, 2, false);\n historyData.pressure = parseBytesToInt(input, i + 14, 2, false) / 10;\n i += 16;\n if (decoded.history == null) {\n decoded.history = [];\n }\n decoded.history.push(historyData);\n }\n }\n\n output.telemetry = 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;\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;", + "tbelDecoder": "var data = decodeToJson(payload); \nvar deviceName = data.deviceInfo.deviceName;\nvar deviceType = data.deviceInfo.deviceProfileName;\nvar groupName = '7-in-1 IAQ Sensor';\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(inputArray) {\n var output = { attributes:{}, telemetry: {} };\n // --- Decoding code --- //\n var input = bytesToExecutionArrayList(inputArray);\n output.telemetry.HEX_bytes = bytesToHex(input);\n\n var historyData = {};\n var decoded = {};\n decoded.hexString = bytesToHex(input);\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 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 = input[i] / 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 (AM307)\n if (channel_id === 0x20 && channel_type === 0xCE) {\n historyData = {};\n historyData.timestamp = parseBytesToInt(input, i, 4, false);\n historyData.temperature = parseBytesToInt(input, i + 4, 2, false) / 10;\n historyData.humidity = parseBytesToInt(input, i + 6, 2, false) / 2;\n historyData.pir = input[i + 8] === 1 ? \"trigger\" : \"idle\";\n historyData.light_level = input[i + 9] === 1;\n historyData.co2 = parseBytesToInt(input, i + 10, 2, false);\n historyData.tvoc = parseBytesToInt(input, i + 12, 2, false);\n historyData.pressure = parseBytesToInt(input, i + 14, 2, false) / 10;\n i += 16;\n if (decoded.history == null) {\n decoded.history = [];\n }\n decoded.history.push(historyData);\n }\n }\n\n output.telemetry = 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;\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": [