{ "name": "Things Stack Industries Uplink Converter for MClimate Vicki", "type": "UPLINK", "integrationType": "TTI", "debugMode": false, "debugSettings": { "failuresEnabled": true, "allEnabled": false, "allEnabledUntil": 0 }, "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": "/**\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 */\nfunction decodePayload(input) {\n // Initialize the output object with empty attributes and telemetry for clarity.\n var result = { attributes: {}, telemetry: {}};\n\n var values = {};\n var timestamp = metadata.ts;\n\n var reason = input[0];\n \n if (reason == 1 || reason == 129) {\n var b6 = input[6]; \n var motorRange = ((b6 & 0x0F) << 8) + input[5]; \n\n var highNibble = b6 / 16; \n var b4 = input[4]; \n\n var hexB4 = String.format(\"%02x\", b4);\n\n var motorPos1 = Character.toLowerCase(Character.forDigit(highNibble, 16));\n\n var combinedHex = \"\" + motorPos1 + hexB4; \n\n var motorPosition = Integer.parseInt(combinedHex, 16); \n motorPosition = motorPosition & 0xFFF; \n\n var batteryTmp = (input[7] >> 4) & 0x0F; \n var batteryVoltage = 2 + batteryTmp * 0.1;\n \n var b7 = input[7]; \n result.attributes.openWindow = ((b7 >> 3) & 0x01) == 1; \n result.attributes.highMotorConsumption = ((b7 >> 2) & 0x01) == 1; \n result.attributes.lowMotorConsumption = ((b7 >> 1) & 0x01) == 1; \n result.attributes.brokenSensor = (b7 & 0x01) == 1; \n\n var b8 = input[8]; \n result.attributes.childLock = ((b8 >> 7) & 0x01) == 1; \n result.attributes.calibrationFailed = ((b8 >> 6) & 0x01) == 1; \n result.attributes.attachedBackplate = ((b8 >> 5) & 0x01) == 1; \n result.attributes.perceiveAsOnline = ((b8 >> 4) & 0x01) == 1; \n result.attributes.antiFreezeProtection = ((b8 >> 3) & 0x01) == 1;\n \n var sensorTemp = 0; \n if (reason == 1) { \n sensorTemp = (input[2] * 165) / 256 - 40; \n } \n if (reason == 81) { \n sensorTemp = (input[2] - 28.3) / 5.6; \n } \n\n result.attributes.reason = reason; \n values.targetTemperature = input[1]; \n values.sensorTemperature = toFixed(sensorTemp, 2); \n values.relativeHumidity = toFixed((input[3] * 100) / 256, 2); \n result.attributes.motorRange = motorRange; \n result.attributes.motorPosition = motorPosition; \n values.batteryVoltage = toFixed(batteryVoltage, 2); \n result.attributes.valveOpenness = motorRange != 0 ? Math.round(( 1- Double.parseDouble(motorPosition) / motorRange) * 100) : 0; \n values.targetTemperatureFloat = input[1];\n } else { \n var lastBytes = input.slice(input.length - 9, input.length()); \n var b6 = lastBytes[6];\n var motorRange = ((b6 & 0x0F) << 8) + lastBytes[5];\n var motorPosition = (((b6 >> 4) & 0x0F) << 8) + lastBytes[4];\n var batteryTmp = (lastBytes[7] >> 4) & 0x0F;\n var batteryVoltage = 2 + batteryTmp * 0.1;\n\n var b7l = lastBytes[7];\n var openWindowLA = ((b7l >> 3) & 0x01) == 1;\n var highMotorConsumptionLA = ((b7l >> 2) & 0x01) == 1;\n var lowMotorConsumptionLA = ((b7l >> 1) & 0x01) == 1;\n var brokenSensorLA = ( b7l & 0x01) == 1;\n\n var b8l = lastBytes[8];\n var childLockLA = ((b8l >> 7) & 0x01) == 1;\n var calibrationFailedLA = ((b8l >> 6) & 0x01) == 1;\n var attachedBackplateLA = ((b8l >> 5) & 0x01) == 1;\n var perceiveAsOnlineLA = ((b8l >> 4) & 0x01) == 1;\n var antiFreezeProtectionLA = ((b8l >> 3) & 0x01) == 1;\n\n var sensorTempLA = 0;\n if (lastBytes[0] == 1) { \n sensorTempLA = (lastBytes[2] * 165) / 256 - 40; \n }\n if (lastBytes[0] == 81) { \n sensorTempLA = (lastBytes[2] - 28.33333) / 5.66666; \n }\n\n result.attributes.reason = lastBytes[0];\n values.targetTemperature = lastBytes[1];\n values.sensorTemperature = toFixed(sensorTempLA, 2);\n values.relativeHumidity = toFixed((lastBytes[3] * 100) / 256, 2);\n result.attributes.motorRange = motorRange;\n result.attributes.motorPosition = motorPosition;\n values.batteryVoltage = toFixed(batteryVoltage, 2);\n result.attributes.openWindow = openWindowLA;\n result.attributes.highMotorConsumption = highMotorConsumptionLA;\n result.attributes.lowMotorConsumption = lowMotorConsumptionLA;\n result.attributes.brokenSensor = brokenSensorLA;\n result.attributes.childLock = childLockLA;\n result.attributes.calibrationFailed = calibrationFailedLA;\n result.attributes.attachedBackplate = attachedBackplateLA;\n result.attributes.perceiveAsOnline = perceiveAsOnlineLA;\n result.attributes.antiFreezeProtection = antiFreezeProtectionLA;\n result.attributes.valveOpenness =motorRange != 0 ? toFixed((1 - (motorPosition / motorRange)) * 100, 2) : 0;\n values.targetTemperatureFloat = lastBytes[1];\n }\n\n result.telemetry = {\n ts: timestamp,\n values: values\n };\n\n return result;\n}\n\nvar result = {};\n\nif (result != null && payload.length != 0) {\n result = decodePayload(payload);\n}\n\n// Return the final result object.\nreturn result;", "encoder": null, "tbelEncoder": null, "updateOnlyKeys": [ "eui", "devAddr", "fPort", "bandwidth", "spreadingFactor", "codeRate", "frequency", "tenantId" ], "type": "DEVICE", "name": "Vicki $eui", "profile": "$applicationId", "label": "$deviceId", "customer": "", "group": "", "telemetry": null, "attributes": [ "eui", "devAddr", "fPort", "bandwidth", "codeRate", "frequency", "tenantId", "applicationId", "spreadingFactor", "deviceId", "joinEui", "netId", "clusterId", "clusterAddress" ] }, "additionalInfo": { "description": "" }, "edgeTemplate": false, "converterVersion": 2 }