Sensor
/** * 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; }
This codec is sourced from Milesight-IoT. All rights belong to Milesight-IoT.
This codec is licensed under the GNU General Public License v3 (GPL v3). Modifications, if any, are clearly marked. You are free to use, modify, and distribute the codec under the terms of GPL v3.