切换深色模式
S24 漏水 · 解析脚本
本页只展示 S24 漏水 的 AWTools 型号解析脚本,设备类型为 0C(HEX)。先在上位机核对本机类型与固件,再复制使用。
用于本机业务载荷、受支持启动信息和复位码。载荷格式、参数和事件含义按本机协议章节核对。
完整代码
下面是可直接复制的完整 JavaScript。鼠标移到代码框右上角,点击复制代码即可复制整份脚本,无需打开或下载 JS 文件。代码已整理换行与缩进,便于阅读。
js
function parseBytes(v) {
if (Array.isArray(v)) {
for (var a = [], i = 0; i < v.length; i++) {
var x = Number(v[i]);
if (!Number.isFinite(x) || x < 0 || x > 255 || Math.floor(x) !== x) return [];
a.push(x);
}
return a;
}
var s = String(v || "").trim();
if (s.charAt(0) === "[") try {
return parseBytes(JSON.parse(s));
} catch (e) {
return [];
}
s = s.replace(/^0x/i, "").replace(/[\s,;:-]/g, "");
if (!s || s.length % 2 || /[^0-9a-f]/i.test(s)) return [];
for (var a = [], i = 0; i < s.length; i += 2) a.push(parseInt(s.slice(i, i + 2), 16));
return a;
}
function readInt(b, o, n, s) {
for (var v = 0, z = n; n--; ) v = v * 256 + b[o++];
if (s && v >= Math.pow(2, 8 * z - 1)) v -= Math.pow(2, 8 * z);
return v;
}
function toHex(b) {
for (var a = [], i = 0; i < b.length; i++) a.push(("0" + b[i].toString(16)).slice(-2).toUpperCase());
return a.join(" ");
}
function stripLength(b) {
return b.length > 1 && b[0] === b.length - 1 && b[1] === 12 ? b.slice(1) : b;
}
function scaledValue(v, d, u) {
v = Number((v / d).toFixed(4));
if (!u) return v;
v = String(v).replace(/\.0+$/, "").replace(/(\.\d*?)0+$/, "$1");
return u === "\xB0C" || u === "%" ? v + u : v + " " + u;
}
function batteryLevel(x) {
return { "\u7535\u91CF": Math.max(0, Math.min(4, Math.round(x))) * 25 + "%" };
}
function resetReason(x) {
if (x === 0) return "\u6B63\u5E38\u542F\u52A8";
if (x & 4) return "\u770B\u95E8\u72D7\u91CD\u542F";
if (x & 2) return "\u8F6F\u4EF6\u91CD\u542F";
if (x & 1) return "\u786C\u4EF6\u91CD\u542F";
return "\u672A\u77E5\u91CD\u542F";
}
function getPort(d) {
var a = [d, d.variables, d.tags, d.config], k = ["fPort", "FPort", "fport", "f_port", "port"];
for (var i = 0; i < a.length; i++) for (var j = 0; a[i] && j < k.length; j++) {
var v = a[i][k[j]];
if (v != null && v !== "") return Number(v);
}
}
function decodeUplink(i) {
return { data: decode(i && i.bytes, i && i.fPort) };
}
function encodeDownlink(input) {
var b = input && input.data && input.data.bytes;
if (!Array.isArray(b) || b.length === 0 || b.length > 255) return { errors: ["Provide data.bytes as a non-empty byte array; command encoding is not defined"] };
for (var j = 0; j < b.length; j++) if (typeof b[j] !== "number" || !Number.isFinite(b[j]) || Math.floor(b[j]) !== b[j] || b[j] < 0 || b[j] > 255) return { errors: ["data.bytes must contain integers from 0 to 255"] };
return { bytes: b.slice() };
}
function G6x_Decode(b, d) {
if (typeof d === "string") try {
d = JSON.parse(d);
} catch (e) {
d = {};
}
d = d || {};
var a = parseBytes(b);
if (!a.length) return JSON.stringify({ "\u9519\u8BEF": "\u6570\u636E\u4E3A\u7A7A" });
var p = getPort(d), o = decode(a, p);
if (Object.keys(o).length) return JSON.stringify(o);
if (p === 51 || p === 52 || a[0] === 206 && a[1] === 222) return JSON.stringify({});
return JSON.stringify({ "\u6570\u636E": toHex(a) });
}
function decode(b, p) {
b = stripLength(parseBytes(b));
p = Number(p);
if (!b.length) return {};
if (p === 214 && b.length === 1) return { "\u72B6\u6001": resetReason(b[0]) };
if (b.length !== 4 || b[0] !== 12) return {};
return Object.assign({ "\u8BBE\u5907": "Water Leak", "\u72B6\u6001": b[1] ? "\u62A5\u8B66" : "\u6B63\u5E38", "\u6F0F\u6C34\u72B6\u6001": b[2] ? "\u6F0F\u6C34" : "\u6B63\u5E38" }, batteryLevel(b[3]));
}
var startupRule = { "enabled": true, "fPort": 215, "typeOffset": 0, "versionOffset": 1, "variants": [{ "protocolVersion": 6, "length": 11, "fields": [{ "name": "\u5FC3\u8DF3\u5468\u671F", "offset": 4, "size": 3, "scale": 1, "unit": "s" }] }] }, startupName = "Water Leak", startupType = 12;
var decodeBusiness = decode;
decode = function(b, p) {
var hasPort = p !== void 0 && p !== null && p !== "";
if (false) return {};
if (hasPort && Number(p) !== 215) return decodeBusiness(b, p);
b = parseBytes(b);
if (b[startupRule.typeOffset] !== startupType) return hasPort ? {} : decodeBusiness(b, p);
var variant = null;
var variants = (startupRule.enabled ? startupRule.variants : []).concat(startupRule.decodeOnlyVariants || []);
for (var i = 0; i < variants.length; i++) {
var v = variants[i];
if (b.length === v.length && b[startupRule.versionOffset] === v.protocolVersion) variant = v;
}
if (!variant) return hasPort ? {} : decodeBusiness(b, p);
var d = { "\u8BBE\u5907": startupName, "\u72B6\u6001": "\u4E0A\u7535", "\u914D\u7F6E\u534F\u8BAE\u7248\u672C": b[1], "\u56FA\u4EF6\u7248\u672C": b[2] + "." + b[3] };
for (var j = 0; j < variant.fields.length; j++) {
var f = variant.fields[j];
d[f.name] = scaledValue(readInt(b, f.offset, f.size, f.signed), f.scale, f.unit);
}
return Object.assign(d, batteryLevel(b[b.length - 1]));
};接入方法
- 将代码完整复制到本机对应的平台 JavaScript 解析器,一台设备只使用一份脚本,不拼接其他型号的代码。
- 字节数组入口为
decodeUplink({ fPort, bytes }),返回{ data: {...} };HEX 或字节数组入口为G6x_Decode(payload, { fPort }),返回 JSON 字符串。 - FPort 使用十进制并保留真实接收值。若平台提供 Base64,先解码为字节数组,不把 Base64 文本当 HEX。
- 用本机真实上报核对字段、单位与事件,再接入业务。返回空对象不能当作正常零值。
业务字段多为中文,部分数值带单位字符串;不与其他接入示例的字段名混用。部分 S2 脚本按载荷布局识别业务数据,不能代替平台的端口与配置回复分类。
encodeDownlink 仅校验并透传 data.bytes,不按参数生成控制命令。本页用于上行解析,不需要下发控制来测试。
详细载荷与接入边界见协议与平台解析。脚本样例检查不代表已完成现场设备或平台验收。
需要保存原始文件时,可另外打开原始 JS;正常查看和复制使用上方代码框即可。