Sensor
/** * Payload Encoder * * Copyright 2025 Milesight IoT * * @product VS321 */ 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 ("detect" in payload) { encoded = encoded.concat(detect(payload.detect)); } if ("reset" in payload) { encoded = encoded.concat(reset(payload.reset)); } if ("report_interval" in payload) { encoded = encoded.concat(setReportInterval(payload.report_interval)); } if ("collection_interval" in payload) { encoded = encoded.concat(setCollectionInterval(payload.collection_interval)); } if ("report_type" in payload) { encoded = encoded.concat(setReportType(payload.report_type)); } if ("detection_mode" in payload) { encoded = encoded.concat(setDetectionMode(payload.detection_mode)); } if ("adr_enable" in payload) { encoded = encoded.concat(setAdrEnable(payload.adr_enable)); } if ("temperature_alarm_config" in payload) { encoded = encoded.concat(setTemperatureAlarmConfig(payload.temperature_alarm_config)); } if ("humidity_alarm_config" in payload) { encoded = encoded.concat(setHumidityAlarmConfig(payload.humidity_alarm_config)); } if ("illuminance_alarm_config" in payload) { encoded = encoded.concat(setIlluminanceAlarmConfig(payload.illuminance_alarm_config)); } if ("d2d_key" in payload) { encoded = encoded.concat(setD2DKey(payload.d2d_key)); } if ("d2d_enable" in payload) { encoded = encoded.concat(setD2DEnable(payload.d2d_enable)); } 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 ("history_enable" in payload) { encoded = encoded.concat(setHistoryEnable(payload.history_enable)); } if ("retransmit_enable" in payload) { encoded = encoded.concat(setRetransmitEnable(payload.retransmit_enable)); } if ("retransmit_interval" in payload) { encoded = encoded.concat(setRetransmitInterval(payload.retransmit_interval)); } if ("resend_interval" in payload) { encoded = encoded.concat(setResendInterval(payload.resend_interval)); } if ("fetch_history" in payload) { encoded = encoded.concat(fetchHistory(payload.fetch_history)); } if ("stop_transmit" in payload) { encoded = encoded.concat(stopTransmit(payload.stop_transmit)); } if ("lora_port" in payload) { encoded = encoded.concat(setLoRaPort(payload.lora_port)); } return encoded; } /** * reboot * @param {number} reboot values: (0: no, 1: yes) * @example { "reboot": 1 } */ function reboot(reboot) { var reboot_map = { 0: "no", 1: "yes" }; var reboot_values = getValues(reboot_map); if (reboot_values.indexOf(reboot) === -1) { throw new Error("reboot must be one of " + reboot_values.join(", ")); } if (getValue(reboot_map, reboot) === 0) { return []; } return [0xff, 0x10, 0xff]; } /** * detect * @param {number} detect values: (0: no, 1: yes) * @example { "detection": 1 } */ function detect(detect) { var yes_no_map = { 0: "no", 1: "yes" }; var yes_no_values = getValues(yes_no_map); if (yes_no_values.indexOf(detect) === -1) { throw new Error("detection must be one of " + yes_no_values.join(", ")); } if (getValue(yes_no_map, detect) === 0) { return []; } return [0xf9, 0x6c, 0xff]; } /** * reset * @param {number} reset values: (0: no, 1: yes) * @example { "reset": 1 } */ function reset(reset) { var yes_no_map = { 0: "no", 1: "yes" }; var yes_no_values = getValues(yes_no_map); if (yes_no_values.indexOf(reset) === -1) { throw new Error("reset must be one of " + yes_no_values.join(", ")); } if (getValue(yes_no_map, reset) === 0) { return []; } return [0xf9, 0x6e, 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(); } /** * collection interval * @param {number} collection_interval unit: minute, values: (2: 2min, 5: 5min, 10: 10min, 15: 15min, 30: 30min, 60: 1h) * @example { "collection_interval": 60 } */ function setCollectionInterval(collection_interval) { var interval = [2, 5, 10, 15, 30, 60]; if (interval.indexOf(collection_interval) === -1) { throw new Error("collection_interval must be one of " + interval.join(", ")); } var buffer = new Buffer(4); buffer.writeUInt8(0xff); buffer.writeUInt8(0x02); buffer.writeUInt16LE(collection_interval); return buffer.toBytes(); } /** * set report type * @param {number} report_type values: (0: period, 1: immediately) * @example { "report_type": 0 } */ function setReportType(report_type) { var report_type_map = { 0: "period", 1: "immediately" }; var report_type_values = getValues(report_type_map); if (report_type_values.indexOf(report_type) === -1) { throw new Error("report_type must be one of " + report_type_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xf9); buffer.writeUInt8(0x10); buffer.writeUInt8(getValue(report_type_map, report_type)); return buffer.toBytes(); } /** * setDetectionMode * @param {number} detection_mode values: (0: auto, 1: on) * @example { "detection_mode": 0 } */ function setDetectionMode(detection_mode) { var detection_mode_map = { 0: "auto", 1: "on" }; var detection_mode_values = getValues(detection_mode_map); if (detection_mode_values.indexOf(detection_mode) === -1) { throw new Error("detection_mode must be one of " + detection_mode_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xf9); buffer.writeUInt8(0x6b); buffer.writeUInt8(getValue(detection_mode_map, detection_mode)); return buffer.toBytes(); } /** * adr_enable * @param {number} adr_enable values: (0: disable, 1: enable) * @example { "adr_enable": 1 } */ function setAdrEnable(adr_enable) { var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(adr_enable) === -1) { throw new Error("adr_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x40); buffer.writeUInt8(getValue(enable_map, adr_enable)); return buffer.toBytes(); } /** * set temperature alarm config * @param {object} temperature_alarm_config * @param {number} temperature_alarm_config.condition values: (0: disable, 1: below, 2: above, 3: between, 4: outside) * @param {number} temperature_alarm_config.min_threshold unit: °C * @param {number} temperature_alarm_config.max_threshold unit: °C * @param {number} temperature_alarm_config.lock_time unit: minute * @param {number} temperature_alarm_config.continue_time unit: minute * @example { "temperature_alarm_config": { "condition": 0, "min_threshold": 20, "max_threshold": 25, "lock_time": 10, "continue_time": 10 } } */ function setTemperatureAlarmConfig(temperature_alarm_config) { var condition = temperature_alarm_config.condition; var min_threshold = temperature_alarm_config.min_threshold; var max_threshold = temperature_alarm_config.max_threshold; var lock_time = temperature_alarm_config.lock_time; var continue_time = temperature_alarm_config.continue_time; var condition_map = { 0: "disable", 1: "below", 2: "above", 3: "between", 4: "outside" }; var condition_values = getValues(condition_map); if (condition_values.indexOf(condition) === -1) { throw new Error("temperature_alarm_config.condition must be one of " + condition_values.join(", ")); } var data = 0x00; data |= getValue(condition_map, condition); data |= 1 << 3; // temperature data |= 1 << 6; // reserved var buffer = new Buffer(11); buffer.writeUInt8(0xff); buffer.writeUInt8(0x06); buffer.writeUInt8(data); buffer.writeInt16LE(min_threshold * 10); buffer.writeInt16LE(max_threshold * 10); buffer.writeUInt16LE(lock_time); buffer.writeUInt16LE(continue_time); return buffer.toBytes(); } /** * set humidity alarm config * @param {object} humidity_alarm_config * @param {number} humidity_alarm_config.condition values: (0: disable, 1: below, 2: above, 3: between, 4: outside) * @param {number} humidity_alarm_config.min_threshold unit: % * @param {number} humidity_alarm_config.max_threshold unit: % * @param {number} humidity_alarm_config.lock_time unit: minute * @param {number} humidity_alarm_config.continue_time unit: minute * @example { "humidity_alarm_config": { "condition": 0, "min_threshold": 20, "max_threshold": 25, "lock_time": 10, "continue_time": 10 } } */ function setHumidityAlarmConfig(humidity_alarm_config) { var condition = humidity_alarm_config.condition; var min_threshold = humidity_alarm_config.min_threshold; var max_threshold = humidity_alarm_config.max_threshold; var lock_time = humidity_alarm_config.lock_time; var continue_time = humidity_alarm_config.continue_time; var condition_map = { 0: "disable", 1: "below", 2: "above", 3: "between", 4: "outside" }; var condition_values = getValues(condition_map); if (condition_values.indexOf(condition) === -1) { throw new Error("humidity_alarm_config.condition must be one of " + condition_values.join(", ")); } var data = 0x00; data |= getValue(condition_map, condition); data |= 2 << 3; // humidity data |= 1 << 6; // reserved var buffer = new Buffer(11); buffer.writeUInt8(0xff); buffer.writeUInt8(0x06); buffer.writeUInt8(data); buffer.writeUInt16LE(min_threshold * 2); buffer.writeUInt16LE(max_threshold * 2); buffer.writeUInt16LE(lock_time); buffer.writeUInt16LE(continue_time); return buffer.toBytes(); } /** * set illuminance alarm config * @param {object} illuminance_alarm_config * @param {number} illuminance_alarm_config.condition values: (0: disable, 1: below, 2: above, 3: between, 4: outside) * @param {number} illuminance_alarm_config.min_threshold unit: lux * @param {number} illuminance_alarm_config.max_threshold unit: lux * @param {number} illuminance_alarm_config.lock_time unit: minute * @param {number} illuminance_alarm_config.continue_time unit: minute * @example { "illuminance_alarm_config": { "condition": 0, "min_threshold": 20, "max_threshold": 25, "lock_time": 10, "continue_time": 10 } } */ function setIlluminanceAlarmConfig(illuminance_alarm_config) { var condition = illuminance_alarm_config.condition; var min_threshold = illuminance_alarm_config.min_threshold; var max_threshold = illuminance_alarm_config.max_threshold; var lock_time = illuminance_alarm_config.lock_time; var continue_time = illuminance_alarm_config.continue_time; var condition_map = { 0: "disable", 1: "below", 2: "above", 3: "between", 4: "outside" }; var condition_values = getValues(condition_map); if (condition_values.indexOf(condition) === -1) { throw new Error("illuminance_alarm_config.condition must be one of " + condition_values.join(", ")); } var data = 0x00; data |= getValue(condition_map, condition); data |= 3 << 3; // illuminance data |= 1 << 6; // reserved var buffer = new Buffer(11); buffer.writeUInt8(0xff); buffer.writeUInt8(0x06); buffer.writeUInt8(data); buffer.writeUInt16LE(min_threshold); buffer.writeUInt16LE(max_threshold); buffer.writeUInt16LE(lock_time); buffer.writeUInt16LE(continue_time); 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-zA-Z]+$/.test(d2d_key)) { throw new Error("d2d_key must be hex string [0-9a-zA-Z]"); } var data = hexStringToBytes(d2d_key); var buffer = new Buffer(10); buffer.writeUInt8(0xff); buffer.writeUInt8(0x35); buffer.writeBytes(data); return buffer.toBytes(); } /** * set 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(); } /** * d2d master configuration * @param {object} d2d_master_config * @param {number} d2d_master_config._item.mode * @param {number} d2d_master_config._item.enable values: (0: disable, 1: enable) * @param {string} d2d_master_config._item.d2d_cmd * @param {number} d2d_master_config._item.lora_uplink_enable values: (0: disable, 1: enable) * @param {number} d2d_master_config._item.time_enable values: (0: disable, 1: enable) * @param {number} d2d_master_config._item.time unit: minute * @example { "d2d_master_config": [{ "mode": 0, "enable": 1, "d2d_cmd": "0000", "lora_uplink_enable": 1, "time_enable": 1, "time": 10 }] } */ 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 = { 0: "occupied", 1: "vacant", 2: "bright", 3: "dim", 4: "occupied_bright", 5: "occupied_dim" }; // var mode_values = getValues(mode_map); 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 && enable_values.indexOf(lora_uplink_enable) === -1) { throw new Error("d2d_master_config._item.lora_uplink_enable must be one of " + enable_values.join(", ")); } if (enable && enable_values.indexOf(time_enable) === -1) { throw new Error("d2d_master_config._item.time_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(10); buffer.writeUInt8(0xff); buffer.writeUInt8(0x96); buffer.writeUInt8(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(); } /** * history enable * @param {number} history_enable values: (0: disable, 1: enable) * @example { "history_enable": 1 } */ function setHistoryEnable(history_enable) { var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(history_enable) === -1) { throw new Error("history_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x68); buffer.writeUInt8(getValue(enable_map, history_enable)); return buffer.toBytes(); } /** * retransmit enable * @param {number} retransmit_enable values: (0: disable, 1: enable) * @example { "retransmit_enable": 1 } */ function setRetransmitEnable(retransmit_enable) { var enable_map = { 0: "disable", 1: "enable" }; var enable_values = getValues(enable_map); if (enable_values.indexOf(retransmit_enable) === -1) { throw new Error("retransmit_enable must be one of " + enable_values.join(", ")); } var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x69); buffer.writeUInt8(getValue(enable_map, retransmit_enable)); return buffer.toBytes(); } /** * retransmit interval * @param {number} retransmit_interval unit: second, range: [30, 1200] * @example { "retransmit_interval": 60 } */ function setRetransmitInterval(retransmit_interval) { if (typeof retransmit_interval !== "number") { throw new Error("retransmit_interval must be a number"); } if (retransmit_interval < 30 || retransmit_interval > 1200) { throw new Error("retransmit_interval must be in range [30, 1200]"); } var buffer = new Buffer(5); buffer.writeUInt8(0xff); buffer.writeUInt8(0x6a); buffer.writeUInt8(0x00); buffer.writeUInt16LE(retransmit_interval); return buffer.toBytes(); } /** * set resend interval * @param {number} resend_interval unit: second, range: [30, 1200] * @example { "resend_interval": 600 } */ function setResendInterval(resend_interval) { if (typeof resend_interval !== "number") { throw new Error("resend_interval must be a number"); } if (resend_interval < 30 || resend_interval > 1200) { throw new Error("resend_interval must be between 30 and 1200"); } var buffer = new Buffer(5); buffer.writeUInt8(0xff); buffer.writeUInt8(0x6a); buffer.writeUInt8(0x01); buffer.writeUInt16LE(resend_interval); return buffer.toBytes(); } /** * fetch history * @param {object} fetch_history * @param {number} fetch_history.start_time * @param {number} fetch_history.end_time */ function fetchHistory(fetch_history) { var start_time = fetch_history.start_time; var end_time = fetch_history.end_time || 0; if (typeof start_time !== "number") { throw new Error("start_time must be a number"); } if ("end_time" in fetch_history && typeof end_time !== "number") { throw new Error("end_time must be a number"); } if (end_time === 0) { var buffer = new Buffer(6); buffer.writeUInt8(0xfd); buffer.writeUInt8(0x6b); buffer.writeUInt32LE(start_time); } else { var buffer = new Buffer(10); buffer.writeUInt8(0xfd); buffer.writeUInt8(0x6c); buffer.writeUInt32LE(start_time); buffer.writeUInt32LE(end_time); } return buffer.toBytes(); } /** * stop transmit * @param {number} stop_transmit values: (0: no, 1: yes) * @example { "stop_transmit": 1 } */ function stopTransmit(stop_transmit) { var yes_no_map = { 0: "no", 1: "yes" }; var yes_no_values = getValues(yes_no_map); if (yes_no_values.indexOf(stop_transmit) === -1) { throw new Error("stop_transmit must be one of " + yes_no_values.join(", ")); } if (getValue(yes_no_map, stop_transmit) === 0) { return []; } return [0xfd, 0x6d, 0xff]; } /** * lora application port * @param {number} lora_port range: [0, 255] * @example { "lora_port": 85 } */ function setLoRaPort(lora_port) { var buffer = new Buffer(3); buffer.writeUInt8(0xff); buffer.writeUInt8(0x65); buffer.writeUInt8(lora_port); return buffer.toBytes(); } function getValues(map) { var values = []; if (RAW_VALUE) { for (var key in map) { values.push(parseInt(key)); } } else { for (var key in map) { values.push(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.