Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
356 changes: 356 additions & 0 deletions vendors/milesight/codecs/em400-tld.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,356 @@
/**
* Payload Decoder
*
* Copyright 2024 Milesight IoT
*
* @product EM400-TLD
*/
var RAW_VALUE = 0x00;

/* eslint no-redeclare: "off" */
/* eslint-disable */
// Chirpstack v4
function decodeUplink(input) {
var decoded = milesightDeviceDecode(input.bytes);
return { data: decoded };
}

// Chirpstack v3
function Decode(fPort, bytes) {
return milesightDeviceDecode(bytes);
}

// The Things Network
function Decoder(bytes, port) {
return milesightDeviceDecode(bytes);
}
/* eslint-enable */

function milesightDeviceDecode(bytes) {
var decoded = {};

for (var i = 0; i < bytes.length; ) {
var channel_id = bytes[i++];
var channel_type = bytes[i++];

// IPSO VERSION
if (channel_id === 0xff && channel_type === 0x01) {
decoded.ipso_version = readProtocolVersion(bytes[i]);
i += 1;
}
// HARDWARE VERSION
else if (channel_id === 0xff && channel_type === 0x09) {
decoded.hardware_version = readHardwareVersion(bytes.slice(i, i + 2));
i += 2;
}
// FIRMWARE VERSION
else if (channel_id === 0xff && channel_type === 0x0a) {
decoded.firmware_version = readFirmwareVersion(bytes.slice(i, i + 2));
i += 2;
}
// TSL VERSION
else if (channel_id === 0xff && channel_type === 0xff) {
decoded.tsl_version = readTslVersion(bytes.slice(i, i + 2));
i += 2;
}
// SERIAL NUMBER
else if (channel_id === 0xff && channel_type === 0x16) {
decoded.sn = readSerialNumber(bytes.slice(i, i + 8));
i += 8;
}
// LORAWAN CLASS TYPE
else if (channel_id === 0xff && channel_type === 0x0f) {
decoded.lorawan_class = readLoRaWANClass(bytes[i]);
i += 1;
}
// RESET EVENT
else if (channel_id === 0xff && channel_type === 0xfe) {
decoded.reset_event = readResetEvent(1);
i += 1;
}
// DEVICE STATUS
else if (channel_id === 0xff && channel_type === 0x0b) {
decoded.device_status = readDeviceStatus(1);
i += 1;
}

// BATTERY
else if (channel_id === 0x01 && channel_type === 0x75) {
decoded.battery = readUInt8(bytes[i]);
i += 1;
}
// TEMPERATURE
else if (channel_id === 0x03 && channel_type === 0x67) {
decoded.temperature = readInt16LE(bytes.slice(i, i + 2)) / 10;
i += 2;
}
// DISTANCE
else if (channel_id === 0x04 && channel_type === 0x82) {
decoded.distance = readUInt16LE(bytes.slice(i, i + 2));
i += 2;
}
// POSITION
else if (channel_id === 0x05 && channel_type === 0x00) {
decoded.position = readPositionType(bytes[i]);
i += 1;
}
// TEMPERATURE WITH ABNORMAL
else if (channel_id === 0x83 && channel_type === 0x67) {
decoded.temperature = readInt16LE(bytes.slice(i, i + 2)) / 10;
decoded.temperature_alarm = readAlarmType(bytes[i + 2]);
i += 3;
}
// DISTANCE WITH ALARMING
else if (channel_id === 0x84 && channel_type === 0x82) {
decoded.distance = readUInt16LE(bytes.slice(i, i + 2));
decoded.distance_alarm = readAlarmType(bytes[i + 2]);
i += 3;
}
// DOWNLINK RESPONSE
else if (channel_id === 0xfe || channel_id === 0xff) {
var result = handle_downlink_response(channel_type, bytes, i);
decoded = Object.assign(decoded, result.data);
i = result.offset;
} else {
break;
}
}

return decoded;
}

function handle_downlink_response(channel_type, bytes, offset) {
var decoded = {};

switch (channel_type) {
case 0x02:
decoded.collection_interval = readUInt16LE(bytes.slice(offset, offset + 2));
offset += 2;
break;
case 0x03:
decoded.report_interval = readUInt16LE(bytes.slice(offset, offset + 2));
offset += 2;
break;
case 0x06:
var value = readUInt8(bytes[offset]);
var alarm_type = (value >>> 3) & 0x07;
var condition_type = value & 0x07;
var config = {};
config.condition = readMathConditionType(condition_type);
config.alarm_release_enable = readEnableStatus((value >>> 7) & 0x01);
config.threshold_min = readUInt16LE(bytes.slice(offset + 1, offset + 3));
config.threshold_max = readUInt16LE(bytes.slice(offset + 3, offset + 5));
// skip 4 bytes
offset += 9;
if (alarm_type === 1) {
decoded.standard_mode_alarm_config = config;
} else if (alarm_type === 2) {
decoded.bin_mode_alarm_config = config;
}
break;
case 0x10:
decoded.reboot = readYesNoStatus(1);
offset += 1;
break;
case 0x13:
decoded.install_height_enable = readEnableStatus(bytes[offset]);
offset += 1;
break;
case 0x1c:
decoded.recollection_config = {};
decoded.recollection_config.counts = bytes[offset];
decoded.recollection_config.interval = bytes[offset + 1];
offset += 2;
break;
case 0x28:
decoded.query_device_status = readYesNoStatus(1);
offset += 1;
break;
case 0x3e:
decoded.tilt_linkage_distance_enable = readEnableStatus(bytes[offset]);
offset += 1;
break;
case 0x4a:
decoded.sync_time = readYesNoStatus(1);
offset += 1;
break;
case 0x56:
decoded.tof_detection_enable = readEnableStatus(bytes[offset]);
offset += 1;
break;
case 0x70:
decoded.people_existing_height = readUInt16LE(bytes.slice(offset, offset + 2));
offset += 2;
break;
case 0x71:
decoded.working_mode = readWorkingMode(bytes[offset]);
offset += 1;
break;
case 0x77:
decoded.install_height = readUInt16LE(bytes.slice(offset, offset + 2));
offset += 2;
break;
default:
throw new Error("unknown downlink response");
}

return { data: decoded, offset: offset };
}

function readProtocolVersion(bytes) {
var major = (bytes & 0xf0) >> 4;
var minor = bytes & 0x0f;
return "v" + major + "." + minor;
}

function readHardwareVersion(bytes) {
var major = (bytes[0] & 0xff).toString(16);
var minor = (bytes[1] & 0xff) >> 4;
return "v" + major + "." + minor;
}

function readFirmwareVersion(bytes) {
var major = (bytes[0] & 0xff).toString(16);
var minor = (bytes[1] & 0xff).toString(16);
return "v" + major + "." + minor;
}

function readTslVersion(bytes) {
var major = bytes[0] & 0xff;
var minor = bytes[1] & 0xff;
return "v" + major + "." + minor;
}

function readSerialNumber(bytes) {
var temp = [];
for (var idx = 0; idx < bytes.length; idx++) {
temp.push(("0" + (bytes[idx] & 0xff).toString(16)).slice(-2));
}
return temp.join("");
}

function readLoRaWANClass(type) {
var class_map = {
0: "Class A",
1: "Class B",
2: "Class C",
3: "Class CtoB",
};
return getValue(class_map, type);
}

function readResetEvent(status) {
var status_map = { 0: "normal", 1: "reset" };
return getValue(status_map, status);
}

function readDeviceStatus(status) {
var status_map = { 0: "off", 1: "on" };
return getValue(status_map, status);
}

function readYesNoStatus(status) {
var status_map = { 0: "no", 1: "yes" };
return getValue(status_map, status);
}

function readEnableStatus(status) {
var status_map = { 0: "disable", 1: "enable" };
return getValue(status_map, status);
}

function readPositionType(type) {
var type_map = { 0: "normal", 1: "tilt" };
return getValue(type_map, type);
}

function readAlarmType(type) {
var type_map = { 0: "threshold_alarm_release", 1: "threshold_alarm" };
return getValue(type_map, type);
}

function readMathConditionType(type) {
var type_map = { 0: "disable", 1: "below", 2: "above", 3: "between", 4: "outside" };
return getValue(type_map, type);
}

function readWorkingMode(type) {
var type_map = { 0: "standard", 1: "bin" };
return getValue(type_map, type);
}

/* eslint-disable */
function readUInt8(bytes) {
return bytes & 0xff;
}

function readInt8(bytes) {
var ref = readUInt8(bytes);
return ref > 0x7f ? ref - 0x100 : ref;
}

function readUInt16LE(bytes) {
var value = (bytes[1] << 8) + bytes[0];
return value & 0xffff;
}

function readInt16LE(bytes) {
var ref = readUInt16LE(bytes);
return ref > 0x7fff ? ref - 0x10000 : ref;
}

function readUInt32LE(bytes) {
var value = (bytes[3] << 24) + (bytes[2] << 16) + (bytes[1] << 8) + bytes[0];
return (value & 0xffffffff) >>> 0;
}

function readInt32LE(bytes) {
var ref = readUInt32LE(bytes);
return ref > 0x7fffffff ? ref - 0x100000000 : ref;
}

function getValue(map, key) {
if (RAW_VALUE) return key;

var value = map[key];
if (!value) value = "unknown";
return value;
}

//if (!Object.assign) {
Object.defineProperty(Object, "assign", {
enumerable: false,
configurable: true,
writable: true,
value: function (target) {
"use strict";
if (target == null) {
throw new TypeError("Cannot convert first argument to object");
}

var to = Object(target);
for (var i = 1; i < arguments.length; i++) {
var nextSource = arguments[i];
if (nextSource == null) {
continue;
}
nextSource = Object(nextSource);

var keysArray = Object.keys(Object(nextSource));
for (var nextIndex = 0, len = keysArray.length; nextIndex < len; nextIndex++) {
var nextKey = keysArray[nextIndex];
var desc = Object.getOwnPropertyDescriptor(nextSource, nextKey);
if (desc !== undefined && desc.enumerable) {
// concat array
if (Array.isArray(to[nextKey]) && Array.isArray(nextSource[nextKey])) {
to[nextKey] = to[nextKey].concat(nextSource[nextKey]);
} else {
to[nextKey] = nextSource[nextKey];
}
}
}
}
return to;
},
});
//}
1 change: 1 addition & 0 deletions vendors/milesight/codecs/test_decode_em400-tld.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
1 change: 1 addition & 0 deletions vendors/milesight/codecs/test_encode_em400-tld.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
18 changes: 18 additions & 0 deletions vendors/milesight/devices/milesight-em400-tld.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
[device]
id = "92b180ce-3d9e-4f10-a617-d441a46a509e"
name = "Milesight EM400-TLD"
description = "ToF Laser Distance Sensor"

[[device.firmware]]
version = "1.2"
profiles = [
"EU868-1_0_3.toml",
"US915-1_0_3.toml",
"AU915-1_0_3.toml",
"AS923-1_0_3.toml",
]
codec = "em400-tld.js"

[device.metadata]
product_url = "https://www.milesight.com/iot/product/lorawan-sensor/em400-tld"
documentation_url = "https://www.milesight.com/iot/product/lorawan-sensor/em400-tld"
Loading
Loading