{ "name": "Loriot Uplink Converter for MClimate CO2 Display Lite", "type": "UPLINK", "integrationType": "LORIOT", "debugMode": false, "debugSettings": { "failuresEnabled": true, "allEnabled": false, "allEnabledUntil": 1745573957871 }, "configuration": { "scriptLang": "TBEL", "decoder": "/**\n * Decodes the incoming payload and returns a structured object containing telemetry data and attributes.\n *\n * @param {number[]} input - The raw payload received as an array of bytes.\n * @returns {Object} output - The structured output with decoded telemetry and attributes.\n */\n\nfunction decodePayload(input) {\n // Initialize the output object with empty attributes and telemetry for clarity.\n var result = { attributes: {}, telemetry: {}};\n\n // Decode serial number (SN) from the first 4 bytes of the payload.\n // Press '?' icon in the top right corner to learn more about built in helper functions and capabilities.\n result.attributes.sn = parseBytesToInt(input, 0, 4);\n\n // Extract the timestamp from metadata (represented in milliseconds).\n var timestamp = metadata.ts; // ts from the incoming message.\n\n // Initialize an object to store decoded key/value telemetry data.\n var values = {};\n\n // Decode battery level from the 5th byte of the payload.\n values.battery = parseBytesToInt(input, 4, 1);\n\n // Decode temperature from the 6th and 7th bytes of the payload (divided by 100).\n values.temperature = parseBytesToInt(input, 5, 2) / 100.0;\n\n // Decode saturation from the 8th byte of the payload.\n values.saturation = parseBytesToInt(input, 7, 1);\n\n // Combine the timestamp with values and add it to the telemetry.\n result.telemetry = {\n ts: timestamp,\n values: values\n };\n\n // Return the fully constructed output object.\n return result;\n // Same logic, less code:\n // return {\n // attributes: {\n // sn: parseBytesToInt(input, 0, 4)\n // },\n // telemetry: {\n // ts: metadata.ts,\n // values: {\n // battery: parseBytesToInt(input, 4, 1),\n // temperature: parseBytesToInt(input, 5, 2) / 100.0,\n // saturation: parseBytesToInt(input, 7, 1)\n // }\n // }\n // };\n}\n\nvar result = decodePayload(payload);\n// Uncomment this code block to overwrite values set in the main configuration window. Useful if you extract device/asset/customer/group names from the payload;\n// result.type = 'DEVICE'; // Entity type allows you to choose type of created entity. Can be 'DEVICE' or 'ASSET'.\n// result.name = 'Temperature Sensor'; // Device or asset name (the value must be unique)\n// result.profile = 'IndustrialSensorProfile'; // Device or asset profile name.\n// result.customer = 'MyCustomer'; // If customer is not null - created entity will be assigned to customer with such name.\n// result.group = 'SensorsGroup'; // If group is not null - created entity will be added to the entity group with such name.\n\n// Return the final result object.\nreturn result;\n\n/**\n * Parse a slice of bytes from an array into an integer (big-endian).\n *\n * @param {number[]} input - The array of bytes.\n * @param {number} offset - The starting index.\n * @param {number} length - The number of bytes to convert.\n * @returns {number} - The resulting integer.\n */\nfunction parseBytesToInt(input, offset, length) {\n var result = 0;\n for (var i = offset; i < offset + length; i++) {\n result = (result << 8) | (input[i] & 0xFF);\n }\n return result;\n}", "tbelDecoder": "/**\n * Decodes the incoming payload and returns a structured object containing telemetry data and attributes.\n *\n * @param {byte[]} input - The raw payload received as an array of bytes.\n * @returns {Object} output - The structured output with decoded telemetry and attributes.\n */\n\nfunction decodePayload(input) {\n // Initialize the output object with empty attributes and telemetry for clarity.\n var result = { attributes: {}, telemetry: {}};\n\n\n // Extract the timestamp from metadata (represented in milliseconds).\n var timestamp = metadata.ts; // ts is the timestamp parsed from the incoming message's time, or returns the current time if it cannot be parsed.\n\n var values = {};\n \n if (parseBytesToInt(input, 0, 1) == 1) {\n values = handleKeepalive(input, values);\n } else {\n values = handleResponse(input, values);\n var tail = input.subList(input.size() - 10, input.size());\n values = handleKeepalive(tail, values);\n }\n\n // Combine the timestamp with values and add it to the telemetry.\n result.telemetry = {\n ts: timestamp,\n values: values\n };\n\n // Return the fully constructed output object.\n return result;\n}\n\nvar uplinkDataResult = [];\n\nvar result = decodePayload(payload);\n\nuplinkDataResult.push(result);\n\nvar gatewayDeviceNamePrefix = \"Gateway \";\nvar gatewayDeviceType = \"Lora gateway\";\n\nif (metadata.cmd == \"gw\") {\n foreach( gatewayInfo : metadata.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 name: gatewayDeviceNamePrefix + gatewayInfo.gweui,\n profile: gatewayDeviceType,\n telemetry: {\n \"ts\": gatewayInfo.ts,\n \"values\": addGatewayInfo\n },\n attributes: {\n eui: gatewayInfo.gweui\n }\n };\n uplinkDataResult.add(gatewayInfoMsg);\n }\n}\n\nfunction handleKeepalive(bytes, data) {\n var tempRaw = parseBytesToInt(bytes, 1, 2);\n data.sensorTemperature = (tempRaw - 400) / 10;\n data.relativeHumidity = toFixed(parseBytesToInt(bytes, 3, 1) * 100 / 256, 2);\n var battRaw = parseBytesToInt(bytes, 4, 2);\n data.batteryVoltage = battRaw / 1000;\n var low = parseBytesToInt(bytes, 6, 1);\n var b7 = parseBytesToInt(bytes, 7, 1);\n var high = (b7 & 0xF8) >> 3;\n data.CO2 = high * 256 + low;\n data.powerSourceStatus = b7 & 0x07;\n data.lux = parseBytesToInt(bytes, 8, 2);\n return data;\n}\n\nfunction handleResponse(bytes, data) {\n var commands = [];\n foreach(b : bytes) {\n commands.add(bytesToHex([b]));\n }\n var resp = commands.subList(0, commands.size() - 8);\n var i = 0;\n while (i < resp.size()) {\n var cmd = resp.get(i);\n if (cmd == '04') {\n data.deviceVersions = {\n hardware: parseInt(resp.get(i+1), 16),\n software: parseInt(resp.get(i+2), 16)\n };\n i += 3;\n } else if (cmd == '12') {\n data.keepAliveTime = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '14') {\n data.childLock = (parseInt(resp.get(i+1), 16) == 1);\n i += 2;\n } else if (cmd == '19') {\n data.joinRetryPeriod = parseInt(resp.get(i+1), 16) * 5 / 60;\n i += 2;\n } else if (cmd == '1b') {\n data.uplinkType = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '1f') {\n var gm = parseInt(resp.get(i+1) + resp.get(i+2), 16);\n var mb = parseInt(resp.get(i+3) + resp.get(i+4), 16);\n data.boundaryLevels = { good_medium: gm, medium_bad: mb };\n i += 5;\n } else if (cmd == '1d') {\n var dka = 5;\n var wdpC = (resp.get(i+1) == '00') ? false : (parseInt(resp.get(i+1), 16) * dka + 7);\n var wdpUc= (resp.get(i+2) == '00') ? false : parseInt(resp.get(i+2), 16);\n data.watchDogParams = { wdpC: wdpC, wdpUc: wdpUc };\n i += 3;\n } else if (cmd == '21') {\n data.autoZeroValue = parseInt(resp.get(i+1) + resp.get(i+2), 16);\n i += 3;\n } else if (cmd == '25') {\n data.measurementPeriod = {\n good_zone: parseInt(resp.get(i+1), 16),\n medium_zone: parseInt(resp.get(i+2), 16),\n bad_zone: parseInt(resp.get(i+3), 16)\n };\n i += 4;\n } else if (cmd == '2b') {\n data.autoZeroPeriod = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '34') {\n data.displayRefreshPeriod = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '41') {\n data.currentTemperatureVisibility = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '43') {\n data.humidityVisibility = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '45') {\n data.lightIntensityVisibility = parseInt(resp.get(i+1), 16);\n i += 2;\n } else if (cmd == '80') {\n data.measurementBlindTime = parseInt(resp.get(i+1), 16);\n i += 2;\n } else {\n i += 1;\n }\n }\n return data;\n}\n\n// Return the final result object.\nreturn uplinkDataResult;", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ "eui", "fPort", "frequency", "dr" ], "type": "DEVICE", "name": "CO2 Display Lite $eui", "profile": "", "label": "", "customer": "", "group": "", "telemetry": null, "attributes": [ "eui", "fPort", "frequency" ] }, "additionalInfo": { "description": "" }, "edgeTemplate": false, "converterVersion": 2 }