Multitech

Creation procedure

Import steps

  1. Payload Management → Sensor Definitions → Import Manufacturer: MClimateSensor Type: XX XX - the specific name of the sensor you are currently importing

  2. Select both files:

    • mclimate-aqi-codec.js

    • mclimate-aqi-definition.json

  3. Click Import, then Save & Apply – the gateway will create read-only Input objects for telemetry and writable Value objects for every "downlink": true property.

Multitech BACnet Codec file

// DataCake
function Decoder(bytes, port){
    var decoded = decodeUplink({ bytes: bytes, fPort: port }).data;
    return decoded;
}

// Milesight
function Decode(port, bytes){
    var decoded = decodeUplink({ bytes: bytes, fPort: port }).data;
    return decoded;
}

// The Things Industries / Main
function decodeUplink(input) {
    var bytes = input.bytes;
    var data = {};
    var resultToPass = {};
    var toBool = function (value) { return value == '1' };

    function merge_obj(obj1, obj2) {
        var obj3 = {};
        for (var attrname in obj1) { obj3[attrname] = obj1[attrname]; }
        for (var attrname2 in obj2) { obj3[attrname2] = obj2[attrname2]; }
        return obj3;
    }

    function handleKeepalive(bytes, data){
        var tmp = ("0" + bytes[6].toString(16)).substr(-2);
        var motorRange1 = tmp[1];
        var motorRange2 = ("0" + bytes[5].toString(16)).substr(-2);
        var motorRange = (parseInt(motorRange1, 16) << 8) | parseInt(motorRange2, 16);

        var motorPos2 = ("0" + bytes[4].toString(16)).substr(-2);
        var motorPos1 = tmp[0];
        var motorPosition = (parseInt(motorPos1, 16) << 8) | parseInt(motorPos2, 16);

        var batteryTmp = ("0" + bytes[7].toString(16)).substr(-2)[0];
        var batteryVoltageCalculated = 2 + parseInt("0x" + batteryTmp, 16) * 0.1;

        var decbin = function(number) {
            if (number < 0) {
                number = 0xFFFFFFFF + number + 1;
            }
            number = number.toString(2);
            return "00000000".substr(number.length) + number;
        }
        var byte7Bin = decbin(bytes[7]);
        var openWindow = byte7Bin[4];
        var highMotorConsumption = byte7Bin[5];
        var lowMotorConsumption = byte7Bin[6];
        var brokenSensor = byte7Bin[7];
        var byte8Bin = decbin(bytes[8]);
        var childLock = byte8Bin[0];
        var calibrationFailed = byte8Bin[1];
        var attachedBackplate = byte8Bin[2];
        var perceiveAsOnline = byte8Bin[3];
        var antiFreezeProtection = byte8Bin[4];

        var sensorTemp = 0;
        if (Number(bytes[0].toString(16))  == 1) {
            sensorTemp = (bytes[2] * 165) / 256 - 40;
        }

        if (Number(bytes[0].toString(16)) == 81) {
            sensorTemp = (bytes[2] - 28.33333) / 5.66666;
        }
        data.reason = Number(bytes[0].toString(16));
        data.targetTemperature = Number(bytes[1]);
        data.sensorTemperature = Number(sensorTemp.toFixed(2));
        data.relativeHumidity = Number(((bytes[3] * 100) / 256).toFixed(2));
        data.motorRange = motorRange;
        data.motorPosition = motorPosition;
        data.batteryVoltage = Number(batteryVoltageCalculated.toFixed(2));
        data.openWindow = toBool(openWindow);
        data.highMotorConsumption = toBool(highMotorConsumption);
        data.lowMotorConsumption = toBool(lowMotorConsumption);
        data.brokenSensor = toBool(brokenSensor);
        data.childLock = toBool(childLock);
        data.calibrationFailed = toBool(calibrationFailed);
        data.attachedBackplate = toBool(attachedBackplate);
        data.perceiveAsOnline = toBool(perceiveAsOnline);
        data.antiFreezeProtection = toBool(antiFreezeProtection);
        data.valveOpenness = motorRange != 0 ? Math.round((1-(motorPosition/motorRange))*100) : 0;
        if(!data.hasOwnProperty('targetTemperatureFloat')){
            data.targetTemperatureFloat = parseFloat(bytes[1]);
        }
        return data;
    }
   
    function handleResponse(bytes, data){
        var commands = bytes.map(function(byte, i){
        	return ("0" + byte.toString(16)).substr(-2); 
        });
        commands = commands.slice(0,-9);
        var command_len = 0;

        commands.map(function (command, i) {
            switch (command) {
                case '04':
                    {
                        command_len = 2;
                        var hardwareVersion = commands[i + 1];
                        var softwareVersion = commands[i + 2];
                        var dataK = { 'deviceVersions': { 'hardware': Number(hardwareVersion), 'software': Number(softwareVersion) } };
                        resultToPass = merge_obj(resultToPass, dataK);
                    }
                break;
                case '12':
                    {
                        command_len = 1;
                        var dataC = { 'keepAliveTime': parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, dataC);
                    }
                break;
                case '13':
                    {
                        command_len = 4;
                        var enabled = toBool(parseInt(commands[i + 1], 16));
                        var duration = parseInt(commands[i + 2], 16) * 5;
                        var tmp = ("0" + commands[i + 4].toString(16)).substr(-2);
                        var motorPos2 = ("0" + commands[i + 3].toString(16)).substr(-2);
                        var motorPos1 = tmp[0];
                        var motorPosition = (parseInt(motorPos1, 16) << 8) | parseInt(motorPos2, 16);
                        var delta = Number(tmp[1]);

                        var dataD = { 'openWindowParams': { 'enabled': enabled, 'duration': duration, 'motorPosition': motorPosition, 'delta': delta } };
                        resultToPass = merge_obj(resultToPass, dataD);
                    }
                break;
                case '14':
                    {
                        command_len = 1;
                        var dataB = { 'childLock': toBool(parseInt(commands[i + 1], 16)) };
                        resultToPass = merge_obj(resultToPass, dataB);
                    }
                break;
                case '15':
                    {
                        command_len = 2;
                        var dataA = { 'temperatureRangeSettings': { 'min': parseInt(commands[i + 1], 16), 'max': parseInt(commands[i + 2], 16) } };
                        resultToPass = merge_obj(resultToPass, dataA);
                    }
                break;
                case '16':
                    {
                        command_len = 2;
                        var data = { 'internalAlgoParams': { 'period': parseInt(commands[i + 1], 16), 'pFirstLast': parseInt(commands[i + 2], 16), 'pNext': parseInt(commands[i + 3], 16) } };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '17':
                    {
                        command_len = 2;
                        var dataF = { 'internalAlgoTdiffParams': { 'warm': parseInt(commands[i + 1], 16), 'cold': parseInt(commands[i + 2], 16) } };
                        resultToPass = merge_obj(resultToPass, dataF);
                    }
                break;
                case '18':
                    {
                        command_len = 1;
                        var dataE = { 'operationalMode': parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, dataE);
                    }
                break;
                case '19':
                    {
                        command_len = 1;
                        var commandResponse = parseInt(commands[i + 1], 16);
                        var periodInMinutes = commandResponse * 5 / 60;
                        var dataH = { 'joinRetryPeriod': periodInMinutes };
                        resultToPass = merge_obj(resultToPass, dataH);
                    }
                break;
                case '1b':
                    {
                        command_len = 1;
                        var dataG = { 'uplinkType': parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, dataG);
                    }
                break;
                case '1d':
                    {
                        // get default keepalive if it is not available in data
                        command_len = 2;
                        var wdpC = commands[i + 1] == '00' ? false : parseInt(commands[i + 1], 16);
                        var wdpUc = commands[i + 2] == '00' ? false : parseInt(commands[i + 2], 16);
                        var dataJ = { 'watchDogParams': { 'wdpC': wdpC, 'wdpUc': wdpUc } };
                        resultToPass = merge_obj(resultToPass, dataJ);
                    }
                break;
                case '1f':
                    {
                        command_len = 1;
                        var data = { 'primaryOperationalMode': commands[i + 1] };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '21':
                    {
                        command_len = 6;
                        var data = {'batteryRangesBoundaries':{ 
                            'Boundary1': (parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16), 
                            'Boundary2': (parseInt(commands[i + 3], 16) << 8) | parseInt(commands[i + 4], 16), 
                            'Boundary3': (parseInt(commands[i + 5], 16) << 8) | parseInt(commands[i + 6], 16), 
                        }};
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '23':
                    {
                        command_len = 4;
                        var data = {'batteryRangesOverVoltage':{ 
                            'Range1': parseInt(commands[i + 2], 16), 
                            'Range2': parseInt(commands[i + 3], 16), 
                            'Range3': parseInt(commands[i + 4], 16), 
                        }};
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '27':
                    {
                        command_len = 1;
                        var data = {'OVAC': parseInt(commands[i + 1], 16)};
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '28':
                    {
                        command_len = 1;
                        var data = { 'manualTargetTemperatureUpdate': parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, data);

                    }
                break;
                case '29':
                    {
                        command_len = 2;
                        var data = { 'proportionalAlgoParams': { 'coefficient': parseInt(commands[i + 1], 16), 'period': parseInt(commands[i + 2], 16) } };
                        resultToPass = merge_obj(resultToPass, data);

                    }
                break;
                case '2b':
                    {
                        command_len = 1;
                        var data = { 'algoType': commands[i + 1] };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '36':
                    {
                        command_len = 3;
                        var kp = ((parseInt(commands[i + 1], 16) << 16) | (parseInt(commands[i + 2], 16) << 8) | parseInt(commands[i + 3], 16)) / 131072;
                        var data = { 'proportionalGain': Number(kp).toFixed(5) };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '3d':
                    {
                        command_len = 3;
                        var ki = ((parseInt(commands[i + 1], 16) << 16) | (parseInt(commands[i + 2], 16) << 8) | parseInt(commands[i + 3], 16)) / 131072;
                        var data = { 'integralGain': Number(ki).toFixed(5) };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '3f':
                    {
                        command_len = 2;
                        var data = { 'integralValue' : ((parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16))/10 };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '40':
                    {
                        command_len = 1;
                        var data = { 'piRunPeriod' : parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '42':
                    {
                        command_len = 1;
                        var data = { 'tempHysteresis' : parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '44':
                    {
                        command_len = 2;
                        var data = { 'extSensorTemperature' : ((parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16))/10 };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '46':
                    {
                        command_len = 3;
                        var enabled = toBool(parseInt(commands[i + 1], 16));
                        var duration = parseInt(commands[i + 2], 16) * 5;
                        var delta = parseInt(commands[i + 3], 16) /10;

                        var data = { 'openWindowParams': { 'enabled': enabled, 'duration': duration, 'delta': delta } };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '48':
                    {
                        command_len = 1;
                        var data = { 'forceAttach' : parseInt(commands[i + 1], 16) };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '4a':
                    {
                        command_len = 3;
                        var activatedTemperature = parseInt(commands[i + 1], 16)/10;
                        var deactivatedTemperature = parseInt(commands[i + 2], 16)/10;
                        var targetTemperature = parseInt(commands[i + 3], 16);

                        var data = { 'antiFreezeParams': { 'activatedTemperature': activatedTemperature, 'deactivatedTemperature': deactivatedTemperature, 'targetTemperature': targetTemperature } };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '4d':
                    {
                        command_len = 2;
                        var data = { 'piMaxIntegratedError' : ((parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16))/10 };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '50':
                    {
                        command_len = 2;
                        var data = { 'effectiveMotorRange': { 'minValveOpenness': 100 - parseInt(commands[i + 2], 16), 'maxValveOpenness': 100 - parseInt(commands[i + 1], 16) } };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '52':
                    {
                        command_len = 2;
                        var data = { 'targetTemperatureFloat' : ((parseInt(commands[i + 1], 16) << 8) | parseInt(commands[i + 2], 16))/10 };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case '54':
                    {
                        command_len = 1;
                        var offset =  (parseInt(commands[i + 1], 16) - 28) * 0.176;
                        var data = { 'temperatureOffset' : offset };
                        resultToPass = merge_obj(resultToPass, data);
                    }
                break;
                case 'a0': {
                    command_len = 4;
                    var fuota_address = (parseInt(commands[i + 1], 16) << 24) | 
                                      (parseInt(commands[i + 2], 16) << 16) | 
                                      (parseInt(commands[i + 3], 16) << 8) | 
                                      parseInt(commands[i + 4], 16);
                    var fuota_address_raw = commands[i + 1] + commands[i + 2] + 
                                          commands[i + 3] + commands[i + 4];
                    
                    data.fuota = { fuota_address: fuota_address, fuota_address_raw: fuota_address_raw };
                    break;
                }
                default:
                    break;
            }
            commands.splice(i,command_len);
        });
        return resultToPass;
    }
    
    if (bytes[0].toString(16) == 1 || bytes[0].toString(16) == 129) {
        data = merge_obj(data, handleKeepalive(bytes, data));
    }else{
        data = merge_obj(data, handleResponse(bytes, data));
        bytes = bytes.slice(-9);
        data = merge_obj(data, handleKeepalive(bytes, data));
    }

    return {
        data: data
    };
}

function encodeDownlink(input) {
  var bytes = [];
  for (let key of Object.keys(input.data)) {
    switch (key) {
      case "setKeepAlive": {
        bytes.push(0x02);
        bytes.push(input.data.setKeepAlive);
        break;
      }
      case "getKeepAliveTime": {
        bytes.push(0x12);
        break;
      }
      case "recalibrateMotor": {
        bytes.push(0x03);
        break;
      }
      case "getDeviceVersions": {
        bytes.push(0x04);
        break;
      }
      case "setOpenWindow": {
        let enabled = Number(input.data.setOpenWindow.enabled);
        let closeTime = parseInt(input.data.setOpenWindow.closeTime / 5);
        let delta = parseInt(input.data.setOpenWindow.delta, 8);
        let motorPosition = input.data.setOpenWindow.motorPosition;
        let motorPositionFirstPart = motorPosition & 0xff;
        let motorPositionSecondPart = (motorPosition >> 8) & 0xff;
        bytes.push(0x06);
        bytes.push(enabled);
        bytes.push(closeTime);
        bytes.push(motorPositionFirstPart);
        bytes.push((motorPositionSecondPart << 4) | delta);
        break;
      }

      case "getOpenWindowParams": {
        bytes.push(0x13);
        break;
      }
      case "setChildLock": {
        bytes.push(0x07);
        bytes.push(Number(input.data.setChildLock));
        break;
      }
      case "getChildLock": {
        bytes.push(0x14);
        break;
      }
      case "setTemperatureRange": {
        bytes.push(0x08);
        bytes.push(input.data.setTemperatureRange.min);
        bytes.push(input.data.setTemperatureRange.max);
        break;
      }
      case "getTemperatureRange": {
        bytes.push(0x15);
        break;
      }
      case "forceClose": {
        bytes.push(0x0b);
        break;
      }
      case "setInternalAlgoParams": {
        bytes.push(0x0c);
        bytes.push(input.data.setInternalAlgoParams.pFirstLast);
        bytes.push(input.data.setInternalAlgoParams.pNext);
        break;
      }
      case "getInternalAlgoParams": {
        bytes.push(0x16);
        break;
      }
      case "setInternalAlgoTdiffParams": {
        bytes.push(0x1a);
        bytes.push(input.data.setInternalAlgoTdiffParams.cold);
        bytes.push(input.data.setInternalAlgoTdiffParams.warm);
        break;
      }
      case "getInternalAlgoTdiffParams": {
        bytes.push(0x17);
        break;
      }
      case "setOperationalMode": {
        bytes.push(0x0d);
        bytes.push(input.data.setOperationalMode);
        break;
      }
      case "getOperationalMode": {
        bytes.push(0x18);
        break;
      }
      case "setTargetTemperature": {
        bytes.push(0x0e);
        bytes.push(input.data.setTargetTemperature);
        break;
      }
      case "setExternalTemperature": {
        bytes.push(0x0f);
        bytes.push(input.data.setExternalTemperature);
        break;
      }
      case "setJoinRetryPeriod": {
        // period should be passed in minutes
        let periodToPass = (input.data.setJoinRetryPeriod * 60) / 5;
        periodToPass = int(periodToPass);
        bytes.push(0x10);
        bytes.push(periodToPass);
        break;
      }
      case "getJoinRetryPeriod": {
        bytes.push(0x19);
        break;
      }
      case "setUplinkType": {
        bytes.push(0x11);
        bytes.push(input.data.setUplinkType);
        break;
      }
      case "getUplinkType": {
        bytes.push(0x1b);
        break;
      }
      case "setTargetTemperatureAndMotorPosition": {
        bytes.push(0x31);
        bytes.push(
          input.data.setTargetTemperatureAndMotorPosition.motorPosition
        );
        bytes.push(
          input.data.setTargetTemperatureAndMotorPosition.targetTemperature
        );
        break;
      }
      case "setWatchDogParams": {
        bytes.push(0x1c);
        bytes.push(input.data.setWatchDogParams.confirmedUplinks);
        bytes.push(input.data.setWatchDogParams.unconfirmedUplinks);
        break;
      }
      case "getWatchDogParams": {
        bytes.push(0x1d);
        break;
      }
      case "setPrimaryOperationalMode": {
        bytes.push(0x1e);
        bytes.push(input.data.setPrimaryOperationalMode);
        break;
      }
      case "getPrimaryOperationalMode": {
        bytes.push(0x1f);
        break;
      }
      case "setProportionalAlgorithmParameters": {
        bytes.push(0x2a);
        bytes.push(input.data.setProportionalAlgorithmParameters.coefficient);
        bytes.push(input.data.setProportionalAlgorithmParameters.period);
        break;
      }
      case "getProportionalAlgorithmParameters": {
        bytes.push(0x29);
        break;
      }
      case "setTemperatureControlAlgorithm": {
        bytes.push(0x2c);
        bytes.push(input.data.setTemperatureControlAlgorithm);
        break;
      }
      case "getTemperatureControlAlgorithm": {
        bytes.push(0x2b);
        break;
      }
      case "setMotorPositionOnly": {
        let motorPosition = input.data.setMotorPositionOnly;
        let motorPositionFirstPart = motorPosition & 0xff;
        let motorPositionSecondPart = (motorPosition >> 8) & 0xff;
        bytes.push(0x2d);
        bytes.push(motorPositionSecondPart);
        bytes.push(motorPositionFirstPart);
        break;
      }
      case "deviceReset": {
        bytes.push(0x30);
        break;
      }
      case "setChildLockBehavior": {
        bytes.push(0x35);
        bytes.push(input.data.setChildLockBehavior);
        break;
      }
      case "getChildLockBehavior": {
        bytes.push(0x34);
        break;
      }
      case "setProportionalGain": {
        let kp = Math.round(input.data.setProportionalGain * 131072);
        let kpFirstPart = kp & 0xff;
        let kpSecondPart = (kp >> 8) & 0xff;
        let kpThirdPart = (kp >> 16) & 0xff;
        bytes.push(0x37);
        bytes.push(kpThirdPart);
        bytes.push(kpSecondPart);
        bytes.push(kpFirstPart);
        break;
      }
      case "getProportionalGain": {
        bytes.push(0x36);
        break;
      }
      case "setExternalTemperatureFloat": {
        let temp = input.data.setExternalTemperatureFloat * 10;
        let tempFirstPart = temp & 0xff;
        let tempSecondPart = (temp >> 8) & 0xff;
        bytes.push(0x3c);
        bytes.push(tempSecondPart);
        bytes.push(tempFirstPart);
        break;
      }
      case "setIntegralGain": {
        let ki = Math.round(input.data.setIntegralGain * 131072);

        let kiFirstPart = ki & 0xff;
        let kiSecondPart = (ki >> 8) & 0xff;
        let kiThirdPart = (ki >> 16) & 0xff;
        bytes.push(0x3e);
        bytes.push(kiThirdPart);
        bytes.push(kiSecondPart);
        bytes.push(kiFirstPart);
        break;
      }
      case "getIntegralGain": {
        bytes.push(0x3d);
        break;
      }
      case "setPiRunPeriod": {
        bytes.push(0x41);
        bytes.push(input.data.setPiRunPeriod);
        break;
      }
      case "getPiRunPeriod": {
        bytes.push(0x40);
        break;
      }
      case "setTempHysteresis": {
        let tempHysteresis = input.data.setTempHysteresis * 10;
        bytes.push(0x43);
        bytes.push(tempHysteresis);
        break;
      }
      case "getTempHysteresis": {
        bytes.push(0x42);
        break;
      }
      case "setOpenWindowPrecisely": {
        let enabledValue = input.data.setOpenWindowPrecisely.enabled ? 1 : 0;
        let duration = parseInt(input.data.setOpenWindowPrecisely.duration) / 5;
        let delta = input.data.setOpenWindowPrecisely.delta * 10

        bytes.push(0x45);
        bytes.push(enabledValue);
        bytes.push(duration);
        bytes.push(delta);
        break;
      }
      case "getOpenWindowPrecisely": {
        bytes.push(0x46);
        break;
      }
      case "setForceAttach": {
        bytes.push(0x47);
        bytes.push(input.data.setForceAttach);
        break;
      }
      case "getForceAttach": {
        bytes.push(0x48);
        break;
      }
      case "sendCustomHexCommand": {
        let sendCustomHexCommand = input.data.sendCustomHexCommand;
        for (let i = 0; i < sendCustomHexCommand.length; i += 2) {
          const byte = parseInt(sendCustomHexCommand.substr(i, 2), 16);
          bytes.push(byte);
        }
        break;
      }
      default: {
      }
    }
  }

  return {
    bytes: bytes,
    fPort: 1,
    warnings: [],
    errors: [],
  };
}

function decodeDownlink(input) {
  return {
    data: {
      bytes: input.bytes,
    },
    warnings: [],
    errors: [],
  };
}

// example downlink commands
// {"getOperationalMode":""} --> 0x18
// {"setTargetTemperature":20} --> 0x0E14
// {"setTemperatureRange":{"min":15,"max":21}} --> 0x080F15
// {"setChildLock":true} --> 0701
// {"sendCustomHexCommand":"080F15"} --> 0x080F15
// {"setOpenWindow":{"enabled": true, "closeTime": 20 , "delta": 3, "motorPosition": 540}}  --> 0x0601041C23

// example Node Red mqtt message
// msg.topic = 'v3/<Application ID>@ttn/devices/<End device ID>/down/push'
// msg.payload = {"downlinks":[{f_port:1,decoded_payload:{setTargetTemperature:20},priority:'NORMAL',confirmed:false}]}

Multitech BACnet Definition file

{
  "description": "MClimate Vicki Smart TRV (Vicki) – BACnet mapping",
  "properties": {
    
    "reason"                     : { "type": "uint8" },
    "targetTemperature"          : { "type": "uint8",  "units": "celsius" },
    "sensorTemperature"          : { "type": "float",  "units": "celsius" },
    "relativeHumidity"           : { "type": "float",  "units": "%" },
    "motorRange"                 : { "type": "uint16" },
    "motorPosition"              : { "type": "uint16" },
    "batteryVoltage"             : { "type": "float",  "units": "V" },
    "openWindow"                 : { "type": "bool"  },
    "highMotorConsumption"       : { "type": "bool"  },
    "lowMotorConsumption"        : { "type": "bool"  },
    "brokenSensor"               : { "type": "bool"  },
    "childLock"                  : { "type": "bool"  },
    "calibrationFailed"          : { "type": "bool"  },
    "attachedBackplate"          : { "type": "bool"  },
    "perceiveAsOnline"           : { "type": "bool"  },
    "antiFreezeProtection"       : { "type": "bool"  },
    "valveOpenness"              : { "type": "uint8", "units": "%" },
    "targetTemperatureFloat"     : { "type": "float", "units": "celsius" },

    "deviceVersions"             : { "type": "object" },
    "keepAliveTime"              : { "type": "uint8",  "units": "minutes" },
    "openWindowParams"           : { "type": "object" },
    "temperatureRangeSettings"   : { "type": "object" },
    "internalAlgoParams"         : { "type": "object" },
    "internalAlgoTdiffParams"    : { "type": "object" },
    "operationalMode"            : { "type": "uint8" },
    "joinRetryPeriod"            : { "type": "float",  "units": "minutes" },
    "uplinkType"                 : { "type": "uint8" },
    "watchDogParams"             : { "type": "object" },
    "primaryOperationalMode"     : { "type": "uint8" },
    "batteryRangesBoundaries"    : { "type": "object" },
    "batteryRangesOverVoltage"   : { "type": "object" },
    "OVAC"                       : { "type": "uint8" },
    "manualTargetTemperatureUpdate": { "type": "uint8", "units": "celsius" },
    "proportionalAlgoParams"     : { "type": "object" },
    "algoType"                   : { "type": "string" },
    "proportionalGain"           : { "type": "float" },
    "integralGain"               : { "type": "float" },
    "integralValue"              : { "type": "float" },
    "piRunPeriod"                : { "type": "uint8",  "units": "seconds" },
    "tempHysteresis"             : { "type": "float",  "units": "celsius" },
    "extSensorTemperature"       : { "type": "float",  "units": "celsius" },
    "forceAttach"                : { "type": "uint8" },
    "antiFreezeParams"           : { "type": "object" },
    "piMaxIntegratedError"       : { "type": "float" },
    "effectiveMotorRange"        : { "type": "object" },
    "temperatureOffset"          : { "type": "float",  "units": "celsius" },
    "fuota"                      : { "type": "object" },

    "setKeepAlive"                         : { "type": "uint8",  "units": "minutes", "downlink": true },
    "recalibrateMotor"                     : { "type": "bool",                       "downlink": true },
    "setOpenWindow"                        : { "type": "object",                     "downlink": true },
    "setChildLock"                         : { "type": "bool",                       "downlink": true },
    "setTemperatureRange"                  : { "type": "object",                     "downlink": true },
    "forceClose"                           : { "type": "bool",                       "downlink": true },
    "setInternalAlgoParams"                : { "type": "object",                     "downlink": true },
    "setInternalAlgoTdiffParams"           : { "type": "object",                     "downlink": true },
    "setOperationalMode"                   : { "type": "uint8",                      "downlink": true },
    "setTargetTemperature"                 : { "type": "uint8",  "units": "celsius", "downlink": true },
    "setExternalTemperature"               : { "type": "uint8",  "units": "celsius", "downlink": true },
    "setJoinRetryPeriod"                   : { "type": "float",  "units": "minutes", "downlink": true },
    "setUplinkType"                        : { "type": "uint8",                      "downlink": true },
    "setTargetTemperatureAndMotorPosition" : { "type": "object",                     "downlink": true },
    "setWatchDogParams"                    : { "type": "object",                     "downlink": true },
    "setPrimaryOperationalMode"            : { "type": "uint8",                      "downlink": true },
    "setProportionalAlgorithmParameters"   : { "type": "object",                     "downlink": true },
    "setTemperatureControlAlgorithm"       : { "type": "uint8",                      "downlink": true },
    "setMotorPositionOnly"                 : { "type": "uint16",                     "downlink": true },
    "deviceReset"                          : { "type": "bool",                       "downlink": true },
    "setChildLockBehavior"                 : { "type": "uint8",                      "downlink": true },
    "setProportionalGain"                  : { "type": "float",                      "downlink": true },
    "setExternalTemperatureFloat"          : { "type": "float",  "units": "celsius", "downlink": true },
    "setIntegralGain"                      : { "type": "float",                      "downlink": true },
    "setPiRunPeriod"                       : { "type": "uint8",  "units": "seconds", "downlink": true },
    "setTempHysteresis"                    : { "type": "float",  "units": "celsius", "downlink": true },
    "setOpenWindowPrecisely"               : { "type": "object",                     "downlink": true },
    "setForceAttach"                       : { "type": "uint8",                      "downlink": true },
    "sendCustomHexCommand"                 : { "type": "string",                     "downlink": true }
  },

  "decoder": "mclimate-vicki-codec.js",
  "encoder": "mclimate-vicki-codec.js"
}

Last updated

Was this helpful?