/** * Payload Encoder * * Copyright 2025 Milesight IoT * * @product VS341 */ var RAW_VALUE = 0x00; /* eslint no-redeclare: "off" */ /* eslint-disable */ // Chirpstack v4 function encodeDownlink(input) { var encoded = milesightDeviceEncode(input.data); return { bytes: encoded }; } // Chirpstack v3 function Encode(fPort, obj) { return milesightDeviceEncode(obj); } // The Things Network function Encoder(obj, port) { return milesightDeviceEncode(obj); } /* eslint-enable */ function milesightDeviceEncode(payload) { var encoded = []; if ("reboot" in payload) { encoded = encoded.concat(reboot(payload.reboot)); } if ("query_device_status" in payload) { encoded = encoded.concat(queryDeviceStatus(payload.query_device_status)); } if ("report_interval" in payload) { encoded = encoded.concat(setReportInterval(payload.report_interval)); } if ("led_indicator_enable" in payload) { encoded = encoded.concat(setLedIndicatorEnable(payload.led_indicator_enable)); } if ("vacancy_reporting_interval" in payload) { encoded = encoded.concat(setVacancyReportingInterval(payload.vacancy_reporting_interval)); } if ("pir_collect_settings" in payload) { encoded = encoded.concat(setPIRCollectionSettings(payload.pir_collect_settings)); } if ("d2d_master_config" in payload) { for (var i = 0; i < payload.d2d_master_config.length; i++) { encoded = encoded.concat(setD2DMasterConfig(payload.d2d_master_config[i])); } } if ("d2d_enable" in payload) { encoded = encoded.concat(setD2DEnable(payload.d2d_enable)); } if ("d2d_key" in payload) { encoded = encoded.concat(setD2DKey(payload.d2d_key)); } if ("thermopile_collect_settings" in payload) { encoded = encoded.concat(setThermopileCollectSettings(payload.thermopile_collect_settings)); } if ("thermopile_negative_threshold" in payload) { encoded = encoded.concat(setThermopileNegativeThreshold(payload.thermopile_negative_threshold)); } return encoded; } /** * reboot * @param {number} reboot values: (0: no, 1: yes) * @example { "reboot": 1 } */ function reboot(reboot) { var yes_no_map = { 0: "no", 1: "yes" }; var reboot_values = getValues(yes_no_map); if (reboot_values.indexOf(reboot) === -1) { throw new Error("reboot must be one of " + reboot_values.join(", ")); } if (getValue(yes_no_map, reboot) === 0) { return []; } return [0xff, 0x10, 0xff]; } /** * query device status * @param {number} query_device_status values: (0: no, 1: yes) * @example { "query_device_status": 1 } */ function queryDeviceStatus(query_device_status) { var yes_no_map = { 0: "no", 1: "yes" }; var yes_no_values = getValues(yes_no_map); if (yes_no_values.indexOf(query_device_status) === -1) { throw new Error("query_device_status must be one of " + yes_no_values.join(", ")); } if (getValue(yes_no_map, query_device_status) === 0) { return []; } return [0xff, 0x28, 0xff]; } /** * set report interval * @param {number} report_interval unit: minute, range: [1, 1440] * @example { "report_interval": 20 } */ function setReportInterval(report_interval) { if (typeof report_interval !== "number") { throw new Error("report_interval must be a number"); } if (report_interval < 1 || report_interval > 1440) { throw new Error("report_interval must be in range [1, 1440]"); } var buffer = new Buffer(5); buffer.writeUInt8(0xff); buffer.writeUInt8(0x8e); buffer.writeUInt8(0x00); buffer.writeUInt16LE(report_interval); return buffer.toBytes(); } /** * set led indicator enable * @param {number} led_indicator_enable values: (0: disable, 1: enable) * @example { "led_indicator_enable": 1 } */ function setLedIndicatorEnable(led_indicator_enable) { var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(led_indicator_enable) === -1) { throw new Error("led_indicator_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x2f); buffer.writeUInt8(getValue(enable_map, led_indicator_enable)); return buffer.toBytes(); } /** * vacancy reporting interval * @param {number} vacancy_reporting_interval unit: second, range: [0, 100] * @example { "vacancy_reporting_interval": 10 } */ function setVacancyReportingInterval(vacancy_reporting_interval) { var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x95); buffer.writeUInt16LE(vacancy_reporting_interval); return buffer.toBytes(); } /** * pir collection settings * @param {object} pir_collect_settings * @param {number} pir_collect_settings.enable values: (0: disable, 1: enable) * @param {number} pir_collect_settings.count unit: count, range: [1, 5] * @example { "pir_collect_settings": { "enable": 1, "count": 10 } } */ function setPIRCollectionSettings(pir_collect_settings) { var enable = pir_collect_settings.enable; var count = pir_collect_settings.count; var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(enable) === -1) { throw new Error("pir_collect_settings must be one of " + enable_values.join(", ")); } if (count < 1 || count > 5) { throw new Error("pir_collect_settings.count must be in range [1, 5]"); } var buffer = new Buffer(7); buffer.writeUInt8(0xff); buffer.writeUInt8(0x98); buffer.writeUInt8(getValue(enable_map, enable)); buffer.writeUInt16LE(count); buffer.writeUInt16LE(0x0d); return buffer.toBytes(); } /** * d2d master configuration * @param {object} d2d_master_config * @param {number} d2d_master_config.mode values: (1: occupied, 2: vacant) * @param {number} d2d_master_config.enable values: (0: disable, 1: enable) * @param {number} d2d_master_config.lora_uplink_enable values: (0: disable, 1: enable) * @param {string} d2d_master_config.d2d_cmd * @param {number} d2d_master_config.time_enable values: (0: disable, 1: enable) * @param {number} d2d_master_config.time unit: minute * @example { "d2d_master_config": [{ "mode": 0, "uplink_enable": 1, "d2d_cmd": "0000", "lora_uplink_enable": 1 }] } */ function setD2DMasterConfig(d2d_master_config) { var mode = d2d_master_config.mode; var enable = d2d_master_config.enable; var d2d_cmd = d2d_master_config.d2d_cmd; var lora_uplink_enable = d2d_master_config.lora_uplink_enable; var time_enable = d2d_master_config.time_enable; var time = d2d_master_config.time; var mode_map = { 1: "occupied", 2: "vacant" }; var mode_values = getValues(mode_map); if (mode_values.indexOf(mode) === -1) { throw new Error("d2d_master_config._item.mode must be one of " + mode_values.join(", ")); } var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(enable) === -1) { throw new Error("d2d_master_config._item.enable must be one of " + enable_values.join(", ")); } if (enable_values.indexOf(lora_uplink_enable) === -1) { throw new Error("d2d_master_config._item.lora_uplink_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(10); buffer.writeUInt8(0xff); buffer.writeUInt8(0x96); buffer.writeUInt8(getValue(mode_map, mode)); buffer.writeUInt8(getValue(enable_map, enable)); buffer.writeUInt8(getValue(enable_map, lora_uplink_enable)); buffer.writeD2DCommand(d2d_cmd, "0000"); buffer.writeUInt16LE(time); buffer.writeUInt8(getValue(enable_map, time_enable)); return buffer.toBytes(); } /** * d2d enable * @param {number} d2d_enable values: (0: disable, 1: enable) * @example { "d2d_enable": 1 } */ function setD2DEnable(d2d_enable) { var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(d2d_enable) === -1) { throw new Error("d2d_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x84); buffer.writeUInt8(getValue(enable_map, d2d_enable)); return buffer.toBytes(); } /** * set d2d key * @param {string} d2d_key * @example { "d2d_key": "0000000000000000" } */ function setD2DKey(d2d_key) { if (typeof d2d_key !== "string") { throw new Error("d2d_key must be a string"); } if (d2d_key.length !== 16) { throw new Error("d2d_key must be 16 characters"); } if (!/^[0-9a-fA-F]+$/.test(d2d_key)) { throw new Error("d2d_key must be hex string [0-9a-fA-F]"); } var data = hexStringToBytes(d2d_key); var buffer = new Buffer(10); buffer.writeUInt8(0xff); buffer.writeUInt8(0x35); buffer.writeBytes(data); return buffer.toBytes(); } /** * thermopile collect settings * @param {object} thermopile_collect_settings * @param {number} thermopile_collect_settings.enable values: (0: disable, 1: enable) * @param {number} thermopile_collect_settings.separate unit: °C * @param {number} thermopile_collect_settings.threshold_l unit: °C, range: [0, 25.5] * @param {number} thermopile_collect_settings.threshold_h unit: °C, range: [0, 25.5] * @example { "thermopile_collect_settings": { "enable": 1, "separate": 1, "threshold_l": 10, "threshold_h": 20 } } */ function setThermopileCollectSettings(thermopile_collect_settings) { var enable = thermopile_collect_settings.enable; var separate = thermopile_collect_settings.separate; var threshold_l = thermopile_collect_settings.threshold_l; var threshold_h = thermopile_collect_settings.threshold_h; var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(enable) === -1) { throw new Error("thermopile_collect_settings must be one of " + enable_values.join(", ")); } var buffer = new Buffer(7); buffer.writeUInt8(0xff); buffer.writeUInt8(0x99); buffer.writeUInt8(getValue(enable_map, enable)); buffer.writeUInt16LE(separate * 10); buffer.writeUInt8(threshold_l * 10); buffer.writeUInt8(threshold_h * 10); return buffer.toBytes(); } /** * thermopile negative threshold * @param {number} thermopile_negative_threshold unit: °C, range: [-128, -1] * @example { "thermopile_negative_threshold": -10 } */ function setThermopileNegativeThreshold(thermopile_negative_threshold) { var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0xb2); buffer.writeInt8(thermopile_negative_threshold); return buffer.toBytes(); } function getValues(map) { var values = []; for (var key in map) { values.push(RAW_VALUE ? parseInt(key) : map[key]); } return values; } function getValue(map, value) { if (RAW_VALUE) return value; for (var key in map) { if (map[key] === value) { return parseInt(key); } } throw new Error("not match in " + JSON.stringify(map)); } function Buffer(size) { this.buffer = new Array(size); this.offset = 0; for (var i = 0; i < size; i++) { this.buffer[i] = 0; } } Buffer.prototype._write = function (value, byteLength, isLittleEndian) { var offset = 0; for (var index = 0; index < byteLength; index++) { offset = isLittleEndian ? index << 3 : (byteLength - 1 - index) << 3; this.buffer[this.offset + index] = (value >> offset) & 0xff; } }; Buffer.prototype.writeUInt8 = function (value) { this._write(value, 1, true); this.offset += 1; }; Buffer.prototype.writeInt8 = function (value) { this._write(value < 0 ? value + 0x100 : value, 1, true); this.offset += 1; }; Buffer.prototype.writeUInt16LE = function (value) { this._write(value, 2, true); this.offset += 2; }; Buffer.prototype.writeInt16LE = function (value) { this._write(value < 0 ? value + 0x10000 : value, 2, true); this.offset += 2; }; Buffer.prototype.writeUInt24LE = function (value) { this._write(value, 3, true); this.offset += 3; }; Buffer.prototype.writeInt24LE = function (value) { this._write(value < 0 ? value + 0x1000000 : value, 3, true); this.offset += 3; }; Buffer.prototype.writeUInt32LE = function (value) { this._write(value, 4, true); this.offset += 4; }; Buffer.prototype.writeInt32LE = function (value) { this._write(value < 0 ? value + 0x100000000 : value, 4, true); this.offset += 4; }; Buffer.prototype.writeD2DCommand = function (value, defaultValue) { if (typeof value !== "string") { value = defaultValue; } if (value.length !== 4) { throw new Error("d2d_cmd length must be 4"); } this.buffer[this.offset] = parseInt(value.substr(2, 2), 16); this.buffer[this.offset + 1] = parseInt(value.substr(0, 2), 16); this.offset += 2; }; Buffer.prototype.writeBytes = function (bytes) { for (var i = 0; i < bytes.length; i++) { this.buffer[this.offset + i] = bytes[i]; } this.offset += bytes.length; }; Buffer.prototype.toBytes = function () { return this.buffer; }; function hexStringToBytes(hex) { var bytes = []; for (var c = 0; c < hex.length; c += 2) { bytes.push(parseInt(hex.substr(c, 2), 16)); } return bytes; }