> For the complete documentation index, see [llms.txt](https://docs.mclimate.eu/mclimate-lorawan-devices/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.mclimate.eu/mclimate-lorawan-devices/devices/mclimate-ht-+-pir-lite/decoding-and-bacnet-object-mapping/bacnet-codec-and-definition-files/milesight.md).

# Milesight

## Creation procedure

### **Network Server → Payload Codec → Custom Payload Codec**

Copy and paste each of the following in their respective fields:

#### Payload Decoder Function

```js
// Keep-alive frame: 0x81 + 5 data bytes = 6 bytes total.
var KA_CMD = 0x81;
var KA_LEN = 6;

// GET-response DATA length per command byte (excluding the command byte itself).
// Only opcodes this device actually answers with are listed -- see the cheat
// sheet. A wrong entry here would desync every later command in a multi-command
// frame, so each one is cross-checked against the byte table on its own doc page.
var RESP_LEN = {
    0x04: 2,   // hw/sw version   -> <HW nibbles> <SW nibbles>
    0x12: 1,   // keep-alive period, minutes
    0x19: 1,   // join retry period, raw (T[s] = raw * 5)
    0x1b: 1,   // uplink type, 0 unconfirmed / 1 confirmed
    0x1d: 2,   // watchdog <confirmed uplinks> <unconfirmed hours>
    0x37: 1,   // PIR sensor enabled, 0 disabled / 1 enabled
    0x39: 2,   // occupancy timeout, seconds 15:8 / 7:0
    0x3f: 1,   // PIR sensitivity, 12..255
    0xa4: 1    // LoRaWAN region, 0 EU868 / 1 AS923 / 2 AU915 / 3 US915
};

// 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) {
    try {
        var bytes = input.bytes;
        var data = {};

        // ---- keep-alive (0x81) --------------------------------------------
        // byte0 : 0x81
        // byte1 : bits 7:3 RFU | bit 2 Occupied | bits 1:0 T[9:8]
        // byte2 : T[7:0]            t[C]  = (T[9:0] - 400) / 10
        // byte3 : RH                RH[%] = (XX * 100) / 256
        // byte4 : battery           mV    = ((XX * 2200) / 255) + 1600
        // byte5 : PIR trigger count
        function handleKeepalive(b, data) {
            var occupied = (b[1] & 0x04) >> 2;

            // FIX D6: emit 0/1 numbers, not JS booleans. The BACnet objects are
            // UINT8-typed; a boolean can arrive at the gateway as "true"/"false"
            // and never coerces cleanly into an analog/binary presentValue.
            data.occupied = occupied;

            var tempRaw = ((b[1] & 0x03) << 8) | b[2];
            data.sensorTemperature = Number(((tempRaw - 400) / 10).toFixed(2));

            data.relativeHumidity = Number(((b[3] * 100) / 256).toFixed(2));

            // Battery in VOLTS (raw formula yields mV) so the BACnet object can
            // use the well-established UNITS_VOLTS (5) id -- same convention as
            // the PIR mini / CO2 Display lite / WT testers.
            data.batteryVoltage = Number(((((b[4] * 2200) / 255) + 1600) / 1000).toFixed(2));

            data.pirTriggerCount = b[5];

            // Alias kept for parity with MClimate's published Object Mapping,
            // which exposes both "occupied" and "pirSensorValue". They are the
            // SAME keepalive bit; pirSensorValue is not used by the test
            // registry (it would just re-test one bit twice).
            // FIX D5: the published decoder ALSO wrote the string
            // "Motion detected"/"No motion detected" into `pirSensorStatus`
            // here -- the exact key the 0x37 PIR-enabled config readback uses.
            // Every keepalive would have clobbered that config object with a
            // string. Removed: pirSensorStatus is now response-only.
            data.pirSensorValue = occupied;

            return data;
        }

        // ---- GET responses ------------------------------------------------
        function handleResponse(cmd, b, i, data) {
            switch (cmd) {
                case 0x04:
                    // FIX D2: nibble-decode (primary = byte>>4, secondary =
                    // byte&0x0F) and FLATTEN. The published decoder emitted
                    // data.deviceVersions = {hardware: Number("11"), ...} --
                    // both a nested object (unbindable) and a wrong number
                    // (11 instead of 1.1). The doc page's byte table is
                    // authoritative; its prose example contradicts its own
                    // bytes (0x041211 described as "HW 1.1 / SW 1.0").
                    data.deviceHardwareVersion =
                        ((b[i + 1] >> 4) & 0x0F) + ((b[i + 1] & 0x0F) / 10);
                    data.deviceSoftwareVersion =
                        ((b[i + 2] >> 4) & 0x0F) + ((b[i + 2] & 0x0F) / 10);
                    break;
                case 0x12:
                    data.keepAliveTime = b[i + 1];               // minutes
                    break;
                case 0x19:
                    // raw * 5 seconds -> MINUTES. Doc example 0x19C6 -> 198*5
                    // = 990 s = 16.5 min.
                    data.joinRetryPeriod = Number(((b[i + 1] * 5) / 60).toFixed(4));
                    break;
                case 0x1b:
                    data.uplinkType = b[i + 1];                  // 0 unconf, 1 conf
                    break;
                case 0x1d:
                    // FIX D3: plain numbers for BOTH fields. The published
                    // decoder returned a nested watchDogParams object AND
                    // coerced a value of 0 to the boolean false -- 0 is a
                    // meaningful value here (it DISABLES that watchdog) and a
                    // BACnet analog object cannot hold false. Same deliberate
                    // deviation the FCT (D9) and CO2 Display lite decoders
                    // already established: no raw*5+7-minutes conversion on
                    // the confirmed field, just the raw device value.
                    data.watchDogPeriodConfirmed   = b[i + 1];   // unanswered confirmed uplinks
                    data.watchDogPeriodUnconfirmed = b[i + 2];   // hours
                    break;
                case 0x37:
                    // FIX D4: 0x37 is "PIR sensor enabled" on THIS device and
                    // is bound to pirSensorStatus (the published Object
                    // Mapping's RW "PIR Sensor Enabled" object). The published
                    // decoder bound 0x37 to pirSensorState and reserved
                    // pirSensorStatus for 0x3D -- an opcode HT + PIR lite does
                    // not implement.
                    data.pirSensorStatus = b[i + 1];             // 0 disabled, 1 enabled
                    break;
                case 0x39:
                    // FIX D7: 2-byte big-endian seconds. Doc example 0x39003C
                    // -> 60 s. (The published decoder had the right length
                    // here, but under the broken slice(0,-7) split it was
                    // never reached on a real 6-byte-keepalive frame.)
                    data.occupancyTimeout = (b[i + 1] << 8) | b[i + 2];
                    break;
                case 0x3f:
                    data.pirSensorSensitivity = b[i + 1];        // 12..255, inverted scale
                    break;
                case 0xa4:
                    data.lorawanRegion = b[i + 1];               // 0..3
                    break;
                default:
                    break;
            }
            return data;
        }

        // ---- frame walk ---------------------------------------------------
        // FIX D1 (see header) + FIX D8: the published decoder tested
        // `bytes[0] == 81` -- DECIMAL 81, i.e. 0x51. The keepalive command byte
        // is 0x81 = 129, so that test NEVER matched and every frame fell into
        // the response path. Combined with FIX D9 (slice(0,-7) assumed a
        // 7-byte keepalive; this device's is 6), an 8-byte
        // response+keepalive frame parsed the opcode but read its data byte
        // from beyond the sliced array -> NaN into the BACnet object.
        var i = 0, decodedAny = false;
        while (i < bytes.length) {
            var cmd = bytes[i];
            if (cmd === KA_CMD && (bytes.length - i) >= KA_LEN) {
                handleKeepalive(bytes.slice(i, i + KA_LEN), data);
                i += KA_LEN;
                decodedAny = true;
                continue;
            }
            if (RESP_LEN.hasOwnProperty(cmd) && (bytes.length - i) > RESP_LEN[cmd]) {
                handleResponse(cmd, bytes, i, data);
                i += 1 + RESP_LEN[cmd];
                decodedAny = true;
                continue;
            }
            // Unknown/truncated command: we cannot know its length, so we must
            // NOT advance by one and keep guessing -- that is precisely how a
            // desync corrupts every following value. Stop here and report what
            // was decoded so far.
            data.undecodedTrailingBytes = bytes.length - i;
            break;
        }
        if (!decodedAny) throw new Error('Unhandled data');

        return { data: data };
    } catch (e) {
        throw new Error('Unhandled data');
    }
}

```

#### Payload Encoder Function

```js
function asNumber(value, fallback) {
  if (value === null || value === undefined || value === "") return fallback;
  if (typeof value === "number") return value;
  var n = Number(value);
  return isNaN(n) ? fallback : n;
}

function asBool(value, fallback) {
  if (value === null || value === undefined || value === "") return fallback;
  if (typeof value === "boolean") return value;
  if (typeof value === "number") return value !== 0;
  if (typeof value === "string") {
    var v = value.trim().toLowerCase();
    if (v === "true" || v === "on" || v === "1" || v === "yes" ||
        v === "enabled" || v === "enable" || v === "active" || v === "confirmed") return true;
    if (v === "false" || v === "off" || v === "0" || v === "no" ||
        v === "disabled" || v === "disable" || v === "inactive" || v === "unconfirmed") return false;
  }
  return fallback;
}

function parseMaybeJson(value) {
  if (value && typeof value === "object") return value;
  if (typeof value !== "string") return value;
  try {
    return JSON.parse(value);
  } catch (e) {
    return value;
  }
}

function clampByte(v) {
  v = Math.round(v);
  if (v < 0) v = 0;
  if (v > 255) v = 255;
  return v;
}

function normalizeCall(portOrInput, maybeInput) {
  var fPort = 1;
  var input = {};
  // CRITICAL: JS passes a 2nd undefined arg on single-arg calls; without the
  // !== undefined guard every UG65-style Encode({fPort,data}) call is treated
  // as legacy and emits an EMPTY downlink (16ASPM root cause -- FIX E6).
  var legacy = arguments.length >= 2 && maybeInput !== undefined && maybeInput !== null;

  if (legacy) {
    fPort = portOrInput || 1;
    input = maybeInput || {};
  } else {
    input = portOrInput || {};
    fPort = input.fPort || input.port || 1;
  }

  if (!input.hasOwnProperty("data")) {
    input = { data: input };
  }

  return { legacy: legacy, fPort: fPort, data: input.data || {} };
}

// simple 1-byte GET commands (no data byte): key -> opcode.
// Cross-checked against the HT + PIR lite commands cheat sheet, fw 1.1.
var GET_OPCODES = {
  getDeviceVersions:         0x04,
  getKeepAliveTime:          0x12,
  getJoinRetryPeriod:        0x19,
  getUplinkType:             0x1B,
  getWatchDogParams:         0x1D,
  getPIRSensorStatus:        0x37,  // FIX E3: was 0x3D in the published encoder
  getOccupancyTimeout:       0x39,  // FIX E5: missing from the published encoder
  getPIRSensorSensitivity:   0x3F,
  getLoRaWANRegion:          0xA4   // FIX E5: missing from the published encoder
};

function encodeDownlink(input) {
  var bytes = [];
  var data = (input && input.data) || {};
  var key, i, obj, v;

  for (key in data) {
    if (!data.hasOwnProperty(key)) continue;

    if (GET_OPCODES.hasOwnProperty(key)) {
      bytes.push(GET_OPCODES[key]);
      continue;
    }

    switch (key) {
      case "setKeepAlive": // 0x02 <minutes>. 0x00 invalid. Default 0x0A = 10 min.
        bytes.push(0x02);
        bytes.push(clampByte(asNumber(data[key], 10)));
        break;

      case "setJoinRetryPeriod": // 0x10 <raw>, T[s] = raw*5. Default 0x78 = 120 -> 10 min.
        // FIX E1: the published encoder called the non-existent `int()` here.
        // Input is in MINUTES (matching the decoder's output unit), converted
        // to the device's 5-second raw step. Doc example: 20 min -> 0x10F0.
        bytes.push(0x10);
        bytes.push(clampByte((asNumber(data[key], 10) * 60) / 5));
        break;

      case "setUplinkType": // 0x11 <00 unconfirmed | 01 confirmed>. Default 01.
        bytes.push(0x11);
        bytes.push(asBool(data[key], true) ? 1 : 0);
        break;

      case "setWatchDogParams": // 0x1C <confirmed uplink count> <unconfirmed hours>
        // FIX E2: the published encoder read `SetWatchDogParams` (capital S) and
        // pushed two undefined values. Accepts a JSON object or a JSON string
        // (BACnet characterstring writes arrive as text), and both the
        // confirmed/unconfirmed and confirmedUplinks/unconfirmedUplinks key
        // spellings. Defaults 0x02 / 0x18 (24 h); 0 disables that watchdog.
        obj = parseMaybeJson(data[key]) || {};
        bytes.push(0x1C);
        bytes.push(clampByte(asNumber(
          obj.confirmed !== undefined ? obj.confirmed : obj.confirmedUplinks, 2)));
        bytes.push(clampByte(asNumber(
          obj.unconfirmed !== undefined ? obj.unconfirmed : obj.unconfirmedUplinks, 24)));
        break;

      case "setPIRSensorStatus": // 0x36 <00 disable | 01 enable>. Default 01.
        // FIX E3: was 0x3C in the published encoder.
        bytes.push(0x36);
        bytes.push(asBool(data[key], true) ? 1 : 0);
        break;

      case "setOccupancyTimeout": // 0x38 <s 15:8> <s 7:0>. 15..65535 s. Default 300 (5 min).
        // FIX E5: missing from the published encoder. Doc example 60 s -> 0x38003C.
        v = Math.round(asNumber(data[key], 300));
        if (v < 15) v = 15;
        if (v > 65535) v = 65535;
        bytes.push(0x38);
        bytes.push((v >> 8) & 0xFF);
        bytes.push(v & 0xFF);
        break;

      case "setPIRSensorSensitivity": // 0x3E <12..255>. Default 20. 12 = most sensitive.
        v = Math.round(asNumber(data[key], 20));
        if (v < 12) v = 12;
        if (v > 255) v = 255;
        bytes.push(0x3E);
        bytes.push(v);
        break;

      case "restartDevice": // 0xA5, no data byte. DISRUPTIVE -- skip:true in the registry.
        // FIX E5: missing from the published encoder. The device sleeps ~20 s
        // AFTER receiving this, so a confirmed downlink still gets its ACK out.
        // NOT a factory reset.
        if (asBool(data[key], false)) bytes.push(0xA5);
        break;

      case "sendCustomHexCommand":
        // Raw hex passthrough -- the transport the Python test engine uses for
        // every SET in parameters_HTPIRlite.json.
        var hexStr = String(data[key] || "").replace(/\s+/g, "");
        for (i = 0; i < hexStr.length; i += 2) {
          var b = parseInt(hexStr.substr(i, 2), 16);
          if (!isNaN(b)) bytes.push(b);
        }
        break;

      default:
        // FIX E4: commands the published encoder emitted that HT + PIR lite does
        // NOT implement (0x2E/0x2F uplink-on-button-press, 0x48/0x49 PIR
        // measurement period, 0x4A/0x4B PIR check period, 0x4C/0x4D PIR blind
        // period) are deliberately absent, so an accidental write falls through
        // here and emits nothing instead of an undefined-behaviour downlink.
        break;
    }
  }

  return {
    bytes: bytes,
    fPort: (input && input.fPort) || 1,
    warnings: [],
    errors: []
  };
}

function Encode(portOrInput, maybeInput) {
  var call = normalizeCall(portOrInput, maybeInput);
  var result = encodeDownlink({ fPort: call.fPort, data: call.data });

  // legacy style: Encode(port, input) -> raw bytes
  if (call.legacy) {
    return result.bytes;
  }
  // UG65/UG67 style: Encode(input) -> object
  return result;
}

function Encoder(portOrInput, maybeInput) {
  return Encode(portOrInput, maybeInput);
}

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

```

#### **Object Mapping Function (JSON Function)**

```json
{
    "object": [
        {
            "id": "occupied",
            "name": "Occupied",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "sensorTemperature",
            "name": "Temperature",
            "value": "",
            "unit": "°C",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 62,
            "bacnet_unit_type": "UNITS_DEGREES_CELSIUS"
        },
        {
            "id": "relativeHumidity",
            "name": "Relative Humidity",
            "value": "",
            "unit": "%",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 29,
            "bacnet_unit_type": "UNITS_PERCENT_RELATIVE_HUMIDITY"
        },
        {
            "id": "batteryVoltage",
            "name": "Battery Voltage",
            "value": "",
            "unit": "V",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 5,
            "bacnet_unit_type": "UNITS_VOLTS"
        },
        {
            "id": "pirTriggerCount",
            "name": "PIR Trigger Count",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "pirSensorValue",
            "name": "PIR Motion",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "deviceHardwareVersion",
            "name": "Hardware Version",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "deviceSoftwareVersion",
            "name": "Software Version",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "lorawanRegion",
            "name": "LoRaWAN Region",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "ENUM",
            "value_type": "UINT8",
            "bacnet_type": "multistate_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "EU868"
                },
                {
                    "value": 1,
                    "name": "AS923"
                },
                {
                    "value": 2,
                    "name": "AU915"
                },
                {
                    "value": 3,
                    "name": "US915"
                }
            ]
        },
        {
            "id": "keepAliveTime",
            "name": "Keep-Alive Time",
            "value": "",
            "unit": "min",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 72,
            "bacnet_unit_type": "UNITS_MINUTES"
        },
        {
            "id": "joinRetryPeriod",
            "name": "Join Retry Period",
            "value": "",
            "unit": "min",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 72,
            "bacnet_unit_type": "UNITS_MINUTES"
        },
        {
            "id": "uplinkType",
            "name": "Uplink Type",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "watchDogPeriodConfirmed",
            "name": "Watchdog WDP-C",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "watchDogPeriodUnconfirmed",
            "name": "Watchdog WDP-UC",
            "value": "",
            "unit": "h",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 71,
            "bacnet_unit_type": "UNITS_HOURS"
        },
        {
            "id": "pirSensorStatus",
            "name": "PIR Sensor Enabled",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "occupancyTimeout",
            "name": "Occupancy Timeout",
            "value": "",
            "unit": "s",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT16",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 73,
            "bacnet_unit_type": "UNITS_SECONDS"
        },
        {
            "id": "pirSensorSensitivity",
            "name": "PIR Sensitivity",
            "value": "",
            "unit": "",
            "access_mode": "R",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_input_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "setKeepAlive",
            "name": "Set KeepAlv",
            "value": "",
            "unit": "min",
            "access_mode": "W",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_output_object",
            "bacnet_unit_type_id": 72,
            "bacnet_unit_type": "UNITS_MINUTES"
        },
        {
            "id": "setJoinRetryPeriod",
            "name": "Set JoinRt",
            "value": "",
            "unit": "min",
            "access_mode": "W",
            "data_type": "NUMBER",
            "value_type": "FLOAT",
            "bacnet_type": "analog_output_object",
            "bacnet_unit_type_id": 72,
            "bacnet_unit_type": "UNITS_MINUTES"
        },
        {
            "id": "setOccupancyTimeout",
            "name": "Set Occ Tmout",
            "value": "",
            "unit": "s",
            "access_mode": "W",
            "data_type": "NUMBER",
            "value_type": "UINT16",
            "bacnet_type": "analog_output_object",
            "bacnet_unit_type_id": 73,
            "bacnet_unit_type": "UNITS_SECONDS"
        },
        {
            "id": "setPIRSensorSensitivity",
            "name": "Set PIR Sens",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "NUMBER",
            "value_type": "UINT8",
            "bacnet_type": "analog_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS"
        },
        {
            "id": "setUplinkType",
            "name": "Set Uplink",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Unconfirmed"
                },
                {
                    "value": 1,
                    "name": "Confirmed"
                }
            ]
        },
        {
            "id": "setPIRSensorStatus",
            "name": "Set PIR On",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Disabled"
                },
                {
                    "value": 1,
                    "name": "Enabled"
                }
            ]
        },
        {
            "id": "restartDevice",
            "name": "Restart Dev",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "setWatchDogParams",
            "name": "Set WD Pms",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "TEXT",
            "value_type": "STRING",
            "bacnet_type": "character_string_value_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "max_length": 128
        },
        {
            "id": "sendCustomHexCommand",
            "name": "Send HexCmd",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "TEXT",
            "value_type": "STRING",
            "bacnet_type": "character_string_value_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "max_length": 242
        },
        {
            "id": "getDeviceVersions",
            "name": "Get DevVers",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getKeepAliveTime",
            "name": "Get KeepAlv",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getJoinRetryPeriod",
            "name": "Get JoinRt",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getUplinkType",
            "name": "Get Uplink",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getWatchDogParams",
            "name": "Get WD Pms",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getPIRSensorStatus",
            "name": "Get PIR On",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getOccupancyTimeout",
            "name": "Get Occ Tmout",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getPIRSensorSensitivity",
            "name": "Get PIR Sens",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        },
        {
            "id": "getLoRaWANRegion",
            "name": "Get Region",
            "value": "",
            "unit": "",
            "access_mode": "W",
            "data_type": "BOOL",
            "value_type": "UINT8",
            "bacnet_type": "binary_output_object",
            "bacnet_unit_type_id": 95,
            "bacnet_unit_type": "UNITS_NO_UNITS",
            "values": [
                {
                    "value": 0,
                    "name": "Idle"
                },
                {
                    "value": 1,
                    "name": "Run"
                }
            ]
        }
    ]
}

```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.mclimate.eu/mclimate-lorawan-devices/devices/mclimate-ht-+-pir-lite/decoding-and-bacnet-object-mapping/bacnet-codec-and-definition-files/milesight.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
