diff --git a/data/init/product.json b/data/init/product.json
index ae17c8d9..eb645b65 100755
--- a/data/init/product.json
+++ b/data/init/product.json
@@ -208,5 +208,17 @@
"transparent": false,
"locateUpdateType": "manual",
"createAt": 1649653149339
+}, {
+ "id": 3,
+ "productKey": "dzxYwDHswecMP3pf",
+ "productSecret": "730d1137b05b41f280cebbdd47782dd6",
+ "name": "NB水泵",
+ "category": "OpenIitaPump",
+ "nodeType": 1,
+ "uid": "1",
+ "isOpenLocate": 0,
+ "transparent": false,
+ "locateUpdateType": "manual",
+ "createAt": 1649653149339
}
]
\ No newline at end of file
diff --git a/data/init/thingModel.json b/data/init/thingModel.json
index 054ba94e..dea7ae44 100755
--- a/data/init/thingModel.json
+++ b/data/init/thingModel.json
@@ -1314,5 +1314,2044 @@
"services": [],
"events": []
}
+ },
+ {
+ "id": 18,
+ "productKey": "dzxYwDHswecMP3pf",
+ "model": {
+ "properties": [
+ {
+ "identifier": "WorkMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动",
+ "2": "自检1",
+ "3": "防锈",
+ "4": "防冻",
+ "5": "自检2",
+ "6": "温控"
+ }
+ },
+ "name": "模式",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "Pressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "设置压力",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "Switch",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "85": "关闭",
+ "170": "开启"
+ }
+ },
+ "name": "开关",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "EmtyRunPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "缺水压力",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "StartPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "开启压力",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "WaterT",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温保护",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "WaterTReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温复位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "ClearIceEn",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "关闭",
+ "1": "开启"
+ }
+ },
+ "name": "防冻",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "ErrorMsg",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无",
+ "1": "过压",
+ "2": "欠压",
+ "3": "过流",
+ "4": "电机高温",
+ "5": "IPM高温",
+ "6": "堵转",
+ "7": "温升保护",
+ "8": "启动失败",
+ "9": "缺相",
+ "10": "无-",
+ "11": "软件过流",
+ "12": "缺水",
+ "13": "未激活",
+ "14": "传感器故障",
+ "15": "通信故障"
+ }
+ },
+ "name": "故障信息",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Voltage",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "300"
+ }
+ },
+ "name": "电压",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Electric",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "100",
+ "precision": ""
+ }
+ },
+ "name": "电流",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Power",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "5000"
+ }
+ },
+ "name": "功率",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Speed",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "10000"
+ }
+ },
+ "name": "转速",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "CurrentPressure1",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力1",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "CurrentPressure2",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力2",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "IpmTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "IPM温度",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "MotorTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机温度",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "WaterTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "水温度",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "query",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "查询",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "McuStatus",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "APP",
+ "8": "BootLoader"
+ }
+ },
+ "name": "MCU状态",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Scene",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "增压泵",
+ "1": "回水器",
+ "2": "循环泵"
+ }
+ },
+ "name": "设备场景",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "WarnInfo",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "警告信息",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "ActiveTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "激活时间",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "SensorMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动"
+ }
+ },
+ "name": "传感器组合模式",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "SensorGroup",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无传感器",
+ "1": "水流开关-压力",
+ "2": "单水流开关",
+ "3": "单压力",
+ "4": "双压力"
+ }
+ },
+ "name": "传感器组合",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "ElectronicTMax",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机保护温度",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "ElectronicTMaxReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机复位温度",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "History",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "历史上报",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "Model",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "设备型号",
+ "accessMode": "r"
+ },
+ {
+ "identifier": "HandMode",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "手动模式挡位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "TempGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "温控挡位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "RatioGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "比例挡位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "SpeedGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "速度挡位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "PressureGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "压力挡位",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "SceneMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "节能模式-自动挡",
+ "1": "温控模式-温控模式",
+ "2": "一键热水-恒速模式",
+ "3": "定时模式-恒压模式",
+ "4": "比例模式"
+ }
+ },
+ "name": "回水器/循环泵-设置模式",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "EnergyModeTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "节能模式运行时间",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "WaterTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "水流开关设置",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "HotWaterTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "一键热水运行时间",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "TempSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "温控上下限设置",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "CountDown",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "回水器倒计时",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "Enabled",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "停止",
+ "1": "开启"
+ }
+ },
+ "name": "实时运行状态",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "Time",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "时间戳",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "TimeModeSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "定时模式时间设置",
+ "accessMode": "rw"
+ },
+ {
+ "identifier": "McuVersion",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "Mcu版本号",
+ "accessMode": "rw"
+ }
+ ],
+ "services": [
+ {
+ "identifier": "set",
+ "inputData": [
+ {
+ "identifier": "WorkMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动",
+ "2": "自检1",
+ "3": "防锈",
+ "4": "防冻",
+ "5": "自检2",
+ "6": "温控"
+ }
+ },
+ "name": "模式",
+ "required": false
+ },
+ {
+ "identifier": "Pressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "设置压力",
+ "required": false
+ },
+ {
+ "identifier": "Switch",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "85": "关闭",
+ "170": "开启"
+ }
+ },
+ "name": "开关",
+ "required": false
+ },
+ {
+ "identifier": "EmtyRunPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "缺水压力",
+ "required": false
+ },
+ {
+ "identifier": "StartPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "开启压力",
+ "required": false
+ },
+ {
+ "identifier": "WaterT",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温保护",
+ "required": false
+ },
+ {
+ "identifier": "WaterTReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温复位",
+ "required": false
+ },
+ {
+ "identifier": "ClearIceEn",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "关闭",
+ "1": "开启"
+ }
+ },
+ "name": "防冻",
+ "required": false
+ },
+ {
+ "identifier": "query",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "查询",
+ "required": false
+ },
+ {
+ "identifier": "ActiveTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "激活时间",
+ "required": false
+ },
+ {
+ "identifier": "SensorMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动"
+ }
+ },
+ "name": "传感器组合模式",
+ "required": false
+ },
+ {
+ "identifier": "SensorGroup",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无传感器",
+ "1": "水流开关-压力",
+ "2": "单水流开关",
+ "3": "单压力",
+ "4": "双压力"
+ }
+ },
+ "name": "传感器组合",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMax",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机保护温度",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMaxReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机复位温度",
+ "required": false
+ },
+ {
+ "identifier": "HandMode",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "手动模式挡位",
+ "required": false
+ },
+ {
+ "identifier": "TempGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "温控挡位",
+ "required": false
+ },
+ {
+ "identifier": "RatioGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "比例挡位",
+ "required": false
+ },
+ {
+ "identifier": "SpeedGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "速度挡位",
+ "required": false
+ },
+ {
+ "identifier": "PressureGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "压力挡位",
+ "required": false
+ },
+ {
+ "identifier": "SceneMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "节能模式-自动挡",
+ "1": "温控模式-温控模式",
+ "2": "一键热水-恒速模式",
+ "3": "定时模式-恒压模式",
+ "4": "比例模式"
+ }
+ },
+ "name": "回水器/循环泵-设置模式",
+ "required": false
+ },
+ {
+ "identifier": "EnergyModeTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "节能模式运行时间",
+ "required": false
+ },
+ {
+ "identifier": "WaterTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "水流开关设置",
+ "required": false
+ },
+ {
+ "identifier": "HotWaterTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "一键热水运行时间",
+ "required": false
+ },
+ {
+ "identifier": "TempSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "温控上下限设置",
+ "required": false
+ },
+ {
+ "identifier": "CountDown",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "回水器倒计时",
+ "required": false
+ },
+ {
+ "identifier": "Enabled",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "停止",
+ "1": "开启"
+ }
+ },
+ "name": "实时运行状态",
+ "required": false
+ },
+ {
+ "identifier": "Time",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "时间戳",
+ "required": false
+ },
+ {
+ "identifier": "TimeModeSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "定时模式时间设置",
+ "required": false
+ },
+ {
+ "identifier": "McuVersion",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "Mcu版本号",
+ "required": false
+ }
+ ],
+ "outputData": [],
+ "name": "属性设置"
+ },
+ {
+ "identifier": "get",
+ "inputData": [
+ {
+ "identifier": "propertyName",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "500"
+ }
+ },
+ "name": "属性名",
+ "required": false
+ }
+ ],
+ "outputData": [
+ {
+ "identifier": "WorkMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动",
+ "2": "自检1",
+ "3": "防锈",
+ "4": "防冻",
+ "5": "自检2",
+ "6": "温控"
+ }
+ },
+ "name": "模式",
+ "required": false
+ },
+ {
+ "identifier": "Pressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "设置压力",
+ "required": false
+ },
+ {
+ "identifier": "Switch",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "85": "关闭",
+ "170": "开启"
+ }
+ },
+ "name": "开关",
+ "required": false
+ },
+ {
+ "identifier": "EmtyRunPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "缺水压力",
+ "required": false
+ },
+ {
+ "identifier": "StartPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "开启压力",
+ "required": false
+ },
+ {
+ "identifier": "WaterT",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温保护",
+ "required": false
+ },
+ {
+ "identifier": "WaterTReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温复位",
+ "required": false
+ },
+ {
+ "identifier": "ClearIceEn",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "关闭",
+ "1": "开启"
+ }
+ },
+ "name": "防冻",
+ "required": false
+ },
+ {
+ "identifier": "ErrorMsg",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无",
+ "1": "过压",
+ "2": "欠压",
+ "3": "过流",
+ "4": "电机高温",
+ "5": "IPM高温",
+ "6": "堵转",
+ "7": "温升保护",
+ "8": "启动失败",
+ "9": "缺相",
+ "10": "无-",
+ "11": "软件过流",
+ "12": "缺水",
+ "13": "未激活",
+ "14": "传感器故障",
+ "15": "通信故障"
+ }
+ },
+ "name": "故障信息",
+ "required": false
+ },
+ {
+ "identifier": "Voltage",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "300"
+ }
+ },
+ "name": "电压",
+ "required": false
+ },
+ {
+ "identifier": "Electric",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电流",
+ "required": false
+ },
+ {
+ "identifier": "Power",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "5000"
+ }
+ },
+ "name": "功率",
+ "required": false
+ },
+ {
+ "identifier": "Speed",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "10000"
+ }
+ },
+ "name": "转速",
+ "required": false
+ },
+ {
+ "identifier": "CurrentPressure1",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力1",
+ "required": false
+ },
+ {
+ "identifier": "CurrentPressure2",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力2",
+ "required": false
+ },
+ {
+ "identifier": "IpmTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "IPM温度",
+ "required": false
+ },
+ {
+ "identifier": "MotorTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机温度",
+ "required": false
+ },
+ {
+ "identifier": "WaterTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "水温度",
+ "required": false
+ },
+ {
+ "identifier": "query",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "查询",
+ "required": false
+ },
+ {
+ "identifier": "McuStatus",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "APP",
+ "8": "BootLoader"
+ }
+ },
+ "name": "MCU状态",
+ "required": false
+ },
+ {
+ "identifier": "Scene",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "增压泵",
+ "1": "回水器",
+ "2": "循环泵"
+ }
+ },
+ "name": "设备场景",
+ "required": false
+ },
+ {
+ "identifier": "WarnInfo",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "警告信息",
+ "required": false
+ },
+ {
+ "identifier": "ActiveTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "激活时间",
+ "required": false
+ },
+ {
+ "identifier": "SensorMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动"
+ }
+ },
+ "name": "传感器组合模式",
+ "required": false
+ },
+ {
+ "identifier": "SensorGroup",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无传感器",
+ "1": "水流开关-压力",
+ "2": "单水流开关",
+ "3": "单压力",
+ "4": "双压力"
+ }
+ },
+ "name": "传感器组合",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMax",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机保护温度",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMaxReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机复位温度",
+ "required": false
+ },
+ {
+ "identifier": "History",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "历史上报",
+ "required": false
+ },
+ {
+ "identifier": "Model",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "设备型号",
+ "required": false
+ },
+ {
+ "identifier": "HandMode",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "手动模式挡位",
+ "required": false
+ },
+ {
+ "identifier": "TempGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "温控挡位",
+ "required": false
+ },
+ {
+ "identifier": "RatioGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "比例挡位",
+ "required": false
+ },
+ {
+ "identifier": "SpeedGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "速度挡位",
+ "required": false
+ },
+ {
+ "identifier": "PressureGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "压力挡位",
+ "required": false
+ },
+ {
+ "identifier": "SceneMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "节能模式-自动挡",
+ "1": "温控模式-温控模式",
+ "2": "一键热水-恒速模式",
+ "3": "定时模式-恒压模式",
+ "4": "比例模式"
+ }
+ },
+ "name": "回水器/循环泵-设置模式",
+ "required": false
+ },
+ {
+ "identifier": "EnergyModeTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "节能模式运行时间",
+ "required": false
+ },
+ {
+ "identifier": "WaterTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "水流开关设置",
+ "required": false
+ },
+ {
+ "identifier": "HotWaterTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "一键热水运行时间",
+ "required": false
+ },
+ {
+ "identifier": "TempSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "温控上下限设置",
+ "required": false
+ },
+ {
+ "identifier": "CountDown",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "回水器倒计时",
+ "required": false
+ },
+ {
+ "identifier": "Enabled",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "停止",
+ "1": "开启"
+ }
+ },
+ "name": "实时运行状态",
+ "required": false
+ },
+ {
+ "identifier": "Time",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "时间戳",
+ "required": false
+ },
+ {
+ "identifier": "TimeModeSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "定时模式时间设置",
+ "required": false
+ },
+ {
+ "identifier": "McuVersion",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "Mcu版本号",
+ "required": false
+ }
+ ],
+ "name": "属性获取"
+ }
+ ],
+ "events": [
+ {
+ "identifier": "post",
+ "inputData": [],
+ "outputData": [
+ {
+ "identifier": "WorkMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动",
+ "2": "自检1",
+ "3": "防锈",
+ "4": "防冻",
+ "5": "自检2",
+ "6": "温控"
+ }
+ },
+ "name": "模式",
+ "required": false
+ },
+ {
+ "identifier": "Pressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "设置压力",
+ "required": false
+ },
+ {
+ "identifier": "Switch",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "85": "关闭",
+ "170": "开启"
+ }
+ },
+ "name": "开关",
+ "required": false
+ },
+ {
+ "identifier": "EmtyRunPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10",
+ "precision": "1"
+ }
+ },
+ "name": "缺水压力",
+ "required": false
+ },
+ {
+ "identifier": "StartPressure",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "开启压力",
+ "required": false
+ },
+ {
+ "identifier": "WaterT",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温保护",
+ "required": false
+ },
+ {
+ "identifier": "WaterTReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "35",
+ "max": "100"
+ }
+ },
+ "name": "水温复位",
+ "required": false
+ },
+ {
+ "identifier": "ClearIceEn",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "关闭",
+ "1": "开启"
+ }
+ },
+ "name": "防冻",
+ "required": false
+ },
+ {
+ "identifier": "ErrorMsg",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无",
+ "1": "过压",
+ "2": "欠压",
+ "3": "过流",
+ "4": "电机高温",
+ "5": "IPM高温",
+ "6": "堵转",
+ "7": "温升保护",
+ "8": "启动失败",
+ "9": "缺相",
+ "10": "无-",
+ "11": "软件过流",
+ "12": "缺水",
+ "13": "未激活",
+ "14": "传感器故障",
+ "15": "通信故障"
+ }
+ },
+ "name": "故障信息",
+ "required": false
+ },
+ {
+ "identifier": "Voltage",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "300"
+ }
+ },
+ "name": "电压",
+ "required": false
+ },
+ {
+ "identifier": "Electric",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电流",
+ "required": false
+ },
+ {
+ "identifier": "Power",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "5000"
+ }
+ },
+ "name": "功率",
+ "required": false
+ },
+ {
+ "identifier": "Speed",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "10000"
+ }
+ },
+ "name": "转速",
+ "required": false
+ },
+ {
+ "identifier": "CurrentPressure1",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力1",
+ "required": false
+ },
+ {
+ "identifier": "CurrentPressure2",
+ "dataType": {
+ "type": "float",
+ "specs": {
+ "min": "0",
+ "max": "10"
+ }
+ },
+ "name": "实时压力2",
+ "required": false
+ },
+ {
+ "identifier": "IpmTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "IPM温度",
+ "required": false
+ },
+ {
+ "identifier": "MotorTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机温度",
+ "required": false
+ },
+ {
+ "identifier": "WaterTemperature",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "水温度",
+ "required": false
+ },
+ {
+ "identifier": "query",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "查询",
+ "required": false
+ },
+ {
+ "identifier": "McuStatus",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "APP",
+ "8": "BootLoader"
+ }
+ },
+ "name": "MCU状态",
+ "required": false
+ },
+ {
+ "identifier": "Scene",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "增压泵",
+ "1": "回水器",
+ "2": "循环泵"
+ }
+ },
+ "name": "设备场景",
+ "required": false
+ },
+ {
+ "identifier": "WarnInfo",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "警告信息",
+ "required": false
+ },
+ {
+ "identifier": "ActiveTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "激活时间",
+ "required": false
+ },
+ {
+ "identifier": "SensorMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "自动",
+ "1": "手动"
+ }
+ },
+ "name": "传感器组合模式",
+ "required": false
+ },
+ {
+ "identifier": "SensorGroup",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "无传感器",
+ "1": "水流开关-压力",
+ "2": "单水流开关",
+ "3": "单压力",
+ "4": "双压力"
+ }
+ },
+ "name": "传感器组合",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMax",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机保护温度",
+ "required": false
+ },
+ {
+ "identifier": "ElectronicTMaxReset",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "100"
+ }
+ },
+ "name": "电机复位温度",
+ "required": false
+ },
+ {
+ "identifier": "History",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "历史上报",
+ "required": false
+ },
+ {
+ "identifier": "Model",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "设备型号",
+ "required": false
+ },
+ {
+ "identifier": "HandMode",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "手动模式挡位",
+ "required": false
+ },
+ {
+ "identifier": "TempGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "温控挡位",
+ "required": false
+ },
+ {
+ "identifier": "RatioGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "比例挡位",
+ "required": false
+ },
+ {
+ "identifier": "SpeedGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "速度挡位",
+ "required": false
+ },
+ {
+ "identifier": "PressureGear",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "1",
+ "max": "5"
+ }
+ },
+ "name": "压力挡位",
+ "required": false
+ },
+ {
+ "identifier": "SceneMode",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "节能模式-自动挡",
+ "1": "温控模式-温控模式",
+ "2": "一键热水-恒速模式",
+ "3": "定时模式-恒压模式",
+ "4": "比例模式"
+ }
+ },
+ "name": "回水器/循环泵-设置模式",
+ "required": false
+ },
+ {
+ "identifier": "EnergyModeTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "节能模式运行时间",
+ "required": false
+ },
+ {
+ "identifier": "WaterTime",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "水流开关设置",
+ "required": false
+ },
+ {
+ "identifier": "HotWaterTime",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "一键热水运行时间",
+ "required": false
+ },
+ {
+ "identifier": "TempSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "温控上下限设置",
+ "required": false
+ },
+ {
+ "identifier": "CountDown",
+ "dataType": {
+ "type": "int32",
+ "specs": {
+ "min": "0",
+ "max": "65532"
+ }
+ },
+ "name": "回水器倒计时",
+ "required": false
+ },
+ {
+ "identifier": "Enabled",
+ "dataType": {
+ "type": "enum",
+ "specs": {
+ "0": "停止",
+ "1": "开启"
+ }
+ },
+ "name": "实时运行状态",
+ "required": false
+ },
+ {
+ "identifier": "Time",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "时间戳",
+ "required": false
+ },
+ {
+ "identifier": "TimeModeSet",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "定时模式时间设置",
+ "required": false
+ },
+ {
+ "identifier": "McuVersion",
+ "dataType": {
+ "type": "text",
+ "specs": {
+ "length": "10240"
+ }
+ },
+ "name": "Mcu版本号",
+ "required": false
+ }
+ ],
+ "name": "属性上报"
+ }
+ ]
+ }
}
]
\ No newline at end of file
diff --git a/iot-components/iot-nb-component/pom.xml b/iot-components/iot-nb-component/pom.xml
new file mode 100644
index 00000000..f38cd70d
--- /dev/null
+++ b/iot-components/iot-nb-component/pom.xml
@@ -0,0 +1,106 @@
+
+
+
+ cc.iotkit
+ iot-components
+ ${revision}
+
+ 4.0.0
+
+ iot-nb-component
+
+
+
+
+ io.vertx
+ vertx-core
+
+
+
+ io.vertx
+ vertx-mqtt
+
+
+
+ io.netty
+ netty-codec-mqtt
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.slf4j
+ slf4j-api
+
+
+
+ cc.iotkit
+ iot-common-core
+
+
+
+ cc.iotkit
+ iot-component-base
+
+
+
+ cc.iotkit
+ iot-data-service
+
+
+
+ cc.iotkit
+ iot-script-engine
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-shade-plugin
+ 3.2.4
+
+
+ package
+
+ shade
+
+
+
+
+
+
+ io.vertx:vertx-core
+ io.vertx:vertx-mqtt
+ io.netty:netty-codec-mqtt
+ org.luaj:luaj-jse
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+ ${java.version}
+ true
+ false
+ utf8
+
+
+
+
+
+
\ No newline at end of file
diff --git a/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBConfig.java b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBConfig.java
new file mode 100644
index 00000000..a7278f3b
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBConfig.java
@@ -0,0 +1,27 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.comp.nb;
+
+import lombok.Data;
+
+@Data
+public class NBConfig {
+
+ private int port;
+
+ private String sslKey;
+
+ private String sslCert;
+
+ private boolean ssl;
+
+ private boolean useWebSocket;
+
+}
diff --git a/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBDeviceComponent.java b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBDeviceComponent.java
new file mode 100644
index 00000000..a6956b3e
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBDeviceComponent.java
@@ -0,0 +1,145 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.comp.nb;
+
+import cc.iotkit.common.enums.ErrCode;
+import cc.iotkit.common.exception.BizException;
+import cc.iotkit.common.thing.ThingService;
+import cc.iotkit.common.utils.JsonUtils;
+import cc.iotkit.comp.AbstractDeviceComponent;
+import cc.iotkit.comp.CompConfig;
+import cc.iotkit.comp.model.DeviceState;
+import cc.iotkit.converter.DeviceMessage;
+import cc.iotkit.model.device.message.ThingModelMessage;
+import io.vertx.core.Future;
+import io.vertx.core.Vertx;
+import lombok.*;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.beanutils.BeanUtils;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.CountDownLatch;
+
+@Slf4j
+public class NBDeviceComponent extends AbstractDeviceComponent {
+
+ private Vertx vertx;
+ private CountDownLatch countDownLatch;
+ private String deployedId;
+ private NBVerticle NBVerticle;
+ private final Map deviceChildToParent = new HashMap<>();
+
+ @Override
+ public void create(CompConfig config) {
+ super.create(config);
+ vertx = Vertx.vertx();
+ NBConfig NBConfig = JsonUtils.parseObject(config.getOther(), NBConfig.class);
+ NBVerticle = new NBVerticle(NBConfig);
+ }
+
+ public void start() {
+ try {
+ NBVerticle.setExecutor(getHandler());
+ countDownLatch = new CountDownLatch(1);
+ Future future = vertx.deployVerticle(NBVerticle);
+ future.onSuccess((s -> {
+ deployedId = s;
+ countDownLatch.countDown();
+ }));
+ future.onFailure((e) -> {
+ countDownLatch.countDown();
+ log.error("start mqtt component failed", e);
+ });
+ countDownLatch.await();
+ future.succeeded();
+ } catch (Throwable e) {
+ throw new BizException(ErrCode.COMPONENT_START_ERROR, e);
+ }
+ }
+
+ @SneakyThrows
+ public void stop() {
+ NBVerticle.stop();
+ Future future = vertx.undeploy(deployedId);
+ future.onSuccess(unused -> log.info("stop mqtt component success"));
+ }
+
+ public void destroy() {
+ }
+
+ @Override
+ public void onDeviceStateChange(DeviceState state) {
+ DeviceState.Parent parent = state.getParent();
+ if (parent == null) {
+ return;
+ }
+ Device device = new Device(state.getProductKey(), state.getDeviceName());
+
+ if (DeviceState.STATE_ONLINE.equals(state.getState())) {
+ //保存子设备所属父设备
+ deviceChildToParent.put(device.toString(),
+ new Device(parent.getProductKey(), parent.getDeviceName())
+ );
+ } else {
+ //删除关系
+ deviceChildToParent.remove(device.toString());
+ }
+
+ }
+
+ @Override
+ public DeviceMessage send(DeviceMessage message) {
+ Device child = new Device(message.getProductKey(), message.getDeviceName());
+ //作为子设备查找父设备
+ Device parent = deviceChildToParent.get(child.toString());
+ if (parent == null) {
+ parent = child;
+ }
+
+ Object obj = message.getContent();
+ if (!(obj instanceof Map)) {
+ throw new BizException(ErrCode.DATA_FORMAT_ERROR);
+ }
+ Message msg = new Message();
+ try {
+ BeanUtils.populate(msg, (Map) obj);
+ } catch (Throwable e) {
+ throw new BizException(ErrCode.DATA_FORMAT_ERROR);
+ }
+ log.info("publish topic:{},payload:{}", msg.getTopic(), msg.getPayload());
+ NBVerticle.publish(parent.getProductKey(), parent.getDeviceName(),
+ msg.getTopic(), msg.getPayload());
+
+ return message;
+ }
+
+ @Override
+ public CompConfig getConfig() {
+ return config;
+ }
+
+ @Data
+ public static class Message {
+ private String topic;
+ private String payload;
+ }
+
+ @Data
+ @NoArgsConstructor
+ @AllArgsConstructor
+ @ToString
+ public static class Device {
+ private String productKey;
+ private String deviceName;
+ }
+
+}
diff --git a/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBVerticle.java b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBVerticle.java
new file mode 100644
index 00000000..54425a65
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/java/cc/iotkit/comp/nb/NBVerticle.java
@@ -0,0 +1,218 @@
+/*
+ * +----------------------------------------------------------------------
+ * | Copyright (c) 奇特物联 2021-2022 All rights reserved.
+ * +----------------------------------------------------------------------
+ * | Licensed 未经许可不能去掉「奇特物联」相关版权
+ * +----------------------------------------------------------------------
+ * | Author: xw2sy@163.com
+ * +----------------------------------------------------------------------
+ */
+package cc.iotkit.comp.nb;
+
+import cc.iotkit.common.enums.ErrCode;
+import cc.iotkit.common.exception.BizException;
+import cc.iotkit.comp.IMessageHandler;
+import cc.iotkit.comp.model.ReceiveResult;
+import io.netty.handler.codec.mqtt.MqttConnectReturnCode;
+import io.netty.handler.codec.mqtt.MqttProperties;
+import io.netty.handler.codec.mqtt.MqttQoS;
+import io.vertx.core.AbstractVerticle;
+import io.vertx.core.Future;
+import io.vertx.core.buffer.Buffer;
+import io.vertx.core.net.PemKeyCertOptions;
+import io.vertx.mqtt.*;
+import io.vertx.mqtt.messages.codes.MqttSubAckReasonCode;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang3.StringUtils;
+
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+@Slf4j
+public class NBVerticle extends AbstractVerticle {
+
+ private MqttServer mqttServer;
+
+ private final NBConfig config;
+
+ private IMessageHandler executor;
+
+ private final Map endpointMap = new HashMap<>();
+
+ // 增加一个客户端连接clientid-连接状态池,避免mqtt关闭的时候走异常断开和mqtt断开的handler,导致多次离线消息
+ private static final Map mqttConnectPool = new ConcurrentHashMap<>();
+
+ public NBVerticle(NBConfig config) {
+ this.config = config;
+ }
+
+ public void setExecutor(IMessageHandler executor) {
+ this.executor = executor;
+ }
+
+ @Override
+ public void start() {
+ MqttServerOptions options = new MqttServerOptions()
+ .setPort(config.getPort());
+ if (config.isSsl()) {
+ options = options.setSsl(true)
+ .setKeyCertOptions(new PemKeyCertOptions()
+ .setKeyPath(config.getSslKey())
+ .setCertPath(config.getSslCert()));
+ }
+ options.setUseWebSocket(config.isUseWebSocket());
+
+ mqttServer = MqttServer.create(vertx, options);
+ mqttServer.endpointHandler(endpoint -> {
+ log.info("MQTT client:{} request to connect, clean session = {}", endpoint.clientIdentifier(), endpoint.isCleanSession());
+
+ MqttAuth auth = endpoint.auth();
+ if (auth == null) {
+ return;
+ }
+
+ String clientId = endpoint.clientIdentifier();
+ String authJson = auth.toJson()
+ .put("clientid", clientId).toString();
+
+ log.info("MQTT client auth,clientId:{},username:{},password:{}",
+ clientId, auth.getUsername(), auth.getPassword());
+ try {
+ executor.onReceive(new HashMap<>(), "auth", authJson, (r) -> {
+ if (r == null) {
+ //认证失败
+ endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED);
+ return;
+ }
+ //保存设备与连接关系
+ endpointMap.put(getEndpointKey(r), endpoint);
+ mqttConnectPool.put(clientId, true);
+ });
+ } catch (Throwable e) {
+ log.error("auth failed", e);
+ endpoint.reject(MqttConnectReturnCode.CONNECTION_REFUSED_NOT_AUTHORIZED);
+ return;
+ }
+
+ log.info("MQTT client keep alive timeout = {} ", endpoint.keepAliveTimeSeconds());
+
+ endpoint.accept(false);
+ endpoint.closeHandler((v) -> {
+ log.warn("client connection closed,clientId:{}", clientId);
+ if (Boolean.FALSE.equals(mqttConnectPool.get(clientId))) {
+ return;
+ }
+ executor.onReceive(new HashMap<>(), "disconnect", clientId, (r) -> {
+ //删除设备与连接关系
+ endpointMap.remove(getEndpointKey(r));
+ });
+ }).disconnectMessageHandler(disconnectMessage -> {
+ log.info("Received disconnect from client, reason code = {}", disconnectMessage.code());
+ executor.onReceive(new HashMap<>(), "disconnect", clientId, (r) -> {
+ //删除设备与连接关系
+ endpointMap.remove(getEndpointKey(r));
+ mqttConnectPool.put(clientId, false);
+ });
+ }).subscribeHandler(subscribe -> {
+ List reasonCodes = new ArrayList<>();
+ for (MqttTopicSubscription s : subscribe.topicSubscriptions()) {
+ log.info("Subscription for {},with QoS {}", s.topicName(), s.qualityOfService());
+ try {
+ Map head = new HashMap<>();
+ head.put("topic", s.topicName());
+ executor.onReceive(head, "subscribe", clientId);
+ reasonCodes.add(MqttSubAckReasonCode.qosGranted(s.qualityOfService()));
+ } catch (Throwable e) {
+ log.error("subscribe failed,topic:" + s.topicName(), e);
+ reasonCodes.add(MqttSubAckReasonCode.NOT_AUTHORIZED);
+ }
+ }
+ // ack the subscriptions request
+ endpoint.subscribeAcknowledge(subscribe.messageId(), reasonCodes, MqttProperties.NO_PROPERTIES);
+
+ }).unsubscribeHandler(unsubscribe -> {
+ for (String t : unsubscribe.topics()) {
+ log.info("Unsubscription for {}", t);
+ try {
+ Map head = new HashMap<>();
+ head.put("topic", t);
+ executor.onReceive(head, "unsubscribe", clientId);
+ } catch (Throwable e) {
+ log.error("unsubscribe failed,topic:" + t, e);
+ }
+ }
+ // ack the subscriptions request
+ endpoint.unsubscribeAcknowledge(unsubscribe.messageId());
+ }).publishHandler(message -> {
+ String payload = message.payload().toString(Charset.defaultCharset());
+ log.info("Received message:{}, with QoS {}", payload,
+ message.qosLevel());
+ if (StringUtils.isBlank(payload)) {
+ return;
+ }
+
+ try {
+ Map head = new HashMap<>();
+ String topic = message.topicName();
+ head.put("topic", topic);
+ if (topic.toLowerCase().contains("ota")) {
+ executor.onReceive(head, "ota", payload);
+ } else {
+ executor.onReceive(head, "", payload);
+ }
+ if (message.qosLevel() == MqttQoS.AT_LEAST_ONCE) {
+ endpoint.publishAcknowledge(message.messageId());
+ } else if (message.qosLevel() == MqttQoS.EXACTLY_ONCE) {
+ endpoint.publishReceived(message.messageId());
+ }
+ } catch (Throwable e) {
+ log.error("handler message failed,topic:" + message.topicName(), e);
+ }
+ }).publishReleaseHandler(endpoint::publishComplete);
+ }).listen(ar -> {
+ if (ar.succeeded()) {
+ log.info("MQTT server is listening on port " + ar.result().actualPort());
+ } else {
+ log.error("Error on starting the server", ar.cause());
+ }
+ });
+ }
+
+ @Override
+ public void stop() throws Exception {
+ for (MqttEndpoint endpoint : endpointMap.values()) {
+ executor.onReceive(new HashMap<>(), "disconnect", endpoint.clientIdentifier());
+ }
+ mqttServer.close(voidAsyncResult -> log.info("close mqtt server..."));
+ }
+
+ private String getEndpointKey(ReceiveResult result) {
+ if (result == null) {
+ return null;
+ }
+ return getEndpointKey(result.getProductKey(), result.getDeviceName());
+ }
+
+ private String getEndpointKey(String productKey, String deviceName) {
+ return String.format("%s_%s", productKey, deviceName);
+ }
+
+ public boolean exist(String productKey, String deviceName) {
+ return endpointMap.containsKey(getEndpointKey(productKey, deviceName));
+ }
+
+ public void publish(String productKey, String deviceName, String topic, String msg) {
+ MqttEndpoint endpoint = endpointMap.get(getEndpointKey(productKey, deviceName));
+ if (endpoint == null) {
+ throw new BizException(ErrCode.SEND_DESTINATION_NOT_FOUND);
+ }
+ Future result = endpoint.publish(topic, Buffer.buffer(msg),
+ MqttQoS.AT_LEAST_ONCE, false, false);
+ result.onFailure(e -> log.error("public topic failed", e));
+ result.onSuccess(integer -> log.info("publish success,topic:{},payload:{}", topic, msg));
+ }
+}
diff --git a/iot-components/iot-nb-component/src/main/resources/component.js b/iot-components/iot-nb-component/src/main/resources/component.js
new file mode 100644
index 00000000..7c97759a
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/resources/component.js
@@ -0,0 +1,322 @@
+!function (n) {
+ "use strict";
+
+ function d(n, t) {
+ var r = (65535 & n) + (65535 & t);
+ return (n >> 16) + (t >> 16) + (r >> 16) << 16 | 65535 & r
+ }
+
+ function f(n, t, r, e, o, u) {
+ return d((u = d(d(t, n), d(e, u))) << o | u >>> 32 - o, r)
+ }
+
+ function l(n, t, r, e, o, u, c) {
+ return f(t & r | ~t & e, n, t, o, u, c)
+ }
+
+ function g(n, t, r, e, o, u, c) {
+ return f(t & e | r & ~e, n, t, o, u, c)
+ }
+
+ function v(n, t, r, e, o, u, c) {
+ return f(t ^ r ^ e, n, t, o, u, c)
+ }
+
+ function m(n, t, r, e, o, u, c) {
+ return f(r ^ (t | ~e), n, t, o, u, c)
+ }
+
+ function c(n, t) {
+ var r, e, o, u;
+ n[t >> 5] |= 128 << t % 32, n[14 + (t + 64 >>> 9 << 4)] = t;
+ for (var c = 1732584193, f = -271733879, i = -1732584194, a = 271733878, h = 0; h < n.length; h += 16) c = l(r = c, e = f, o = i, u = a, n[h], 7, -680876936), a = l(a, c, f, i, n[h + 1], 12, -389564586), i = l(i, a, c, f, n[h + 2], 17, 606105819), f = l(f, i, a, c, n[h + 3], 22, -1044525330), c = l(c, f, i, a, n[h + 4], 7, -176418897), a = l(a, c, f, i, n[h + 5], 12, 1200080426), i = l(i, a, c, f, n[h + 6], 17, -1473231341), f = l(f, i, a, c, n[h + 7], 22, -45705983), c = l(c, f, i, a, n[h + 8], 7, 1770035416), a = l(a, c, f, i, n[h + 9], 12, -1958414417), i = l(i, a, c, f, n[h + 10], 17, -42063), f = l(f, i, a, c, n[h + 11], 22, -1990404162), c = l(c, f, i, a, n[h + 12], 7, 1804603682), a = l(a, c, f, i, n[h + 13], 12, -40341101), i = l(i, a, c, f, n[h + 14], 17, -1502002290), c = g(c, f = l(f, i, a, c, n[h + 15], 22, 1236535329), i, a, n[h + 1], 5, -165796510), a = g(a, c, f, i, n[h + 6], 9, -1069501632), i = g(i, a, c, f, n[h + 11], 14, 643717713), f = g(f, i, a, c, n[h], 20, -373897302), c = g(c, f, i, a, n[h + 5], 5, -701558691), a = g(a, c, f, i, n[h + 10], 9, 38016083), i = g(i, a, c, f, n[h + 15], 14, -660478335), f = g(f, i, a, c, n[h + 4], 20, -405537848), c = g(c, f, i, a, n[h + 9], 5, 568446438), a = g(a, c, f, i, n[h + 14], 9, -1019803690), i = g(i, a, c, f, n[h + 3], 14, -187363961), f = g(f, i, a, c, n[h + 8], 20, 1163531501), c = g(c, f, i, a, n[h + 13], 5, -1444681467), a = g(a, c, f, i, n[h + 2], 9, -51403784), i = g(i, a, c, f, n[h + 7], 14, 1735328473), c = v(c, f = g(f, i, a, c, n[h + 12], 20, -1926607734), i, a, n[h + 5], 4, -378558), a = v(a, c, f, i, n[h + 8], 11, -2022574463), i = v(i, a, c, f, n[h + 11], 16, 1839030562), f = v(f, i, a, c, n[h + 14], 23, -35309556), c = v(c, f, i, a, n[h + 1], 4, -1530992060), a = v(a, c, f, i, n[h + 4], 11, 1272893353), i = v(i, a, c, f, n[h + 7], 16, -155497632), f = v(f, i, a, c, n[h + 10], 23, -1094730640), c = v(c, f, i, a, n[h + 13], 4, 681279174), a = v(a, c, f, i, n[h], 11, -358537222), i = v(i, a, c, f, n[h + 3], 16, -722521979), f = v(f, i, a, c, n[h + 6], 23, 76029189), c = v(c, f, i, a, n[h + 9], 4, -640364487), a = v(a, c, f, i, n[h + 12], 11, -421815835), i = v(i, a, c, f, n[h + 15], 16, 530742520), c = m(c, f = v(f, i, a, c, n[h + 2], 23, -995338651), i, a, n[h], 6, -198630844), a = m(a, c, f, i, n[h + 7], 10, 1126891415), i = m(i, a, c, f, n[h + 14], 15, -1416354905), f = m(f, i, a, c, n[h + 5], 21, -57434055), c = m(c, f, i, a, n[h + 12], 6, 1700485571), a = m(a, c, f, i, n[h + 3], 10, -1894986606), i = m(i, a, c, f, n[h + 10], 15, -1051523), f = m(f, i, a, c, n[h + 1], 21, -2054922799), c = m(c, f, i, a, n[h + 8], 6, 1873313359), a = m(a, c, f, i, n[h + 15], 10, -30611744), i = m(i, a, c, f, n[h + 6], 15, -1560198380), f = m(f, i, a, c, n[h + 13], 21, 1309151649), c = m(c, f, i, a, n[h + 4], 6, -145523070), a = m(a, c, f, i, n[h + 11], 10, -1120210379), i = m(i, a, c, f, n[h + 2], 15, 718787259), f = m(f, i, a, c, n[h + 9], 21, -343485551), c = d(c, r), f = d(f, e), i = d(i, o), a = d(a, u);
+ return [c, f, i, a]
+ }
+
+ function i(n) {
+ for (var t = "", r = 32 * n.length, e = 0; e < r; e += 8) t += String.fromCharCode(n[e >> 5] >>> e % 32 & 255);
+ return t
+ }
+
+ function a(n) {
+ var t = [];
+ for (t[(n.length >> 2) - 1] = void 0, e = 0; e < t.length; e += 1) t[e] = 0;
+ for (var r = 8 * n.length, e = 0; e < r; e += 8) t[e >> 5] |= (255 & n.charCodeAt(e / 8)) << e % 32;
+ return t
+ }
+
+ function e(n) {
+ for (var t, r = "0123456789abcdef", e = "", o = 0; o < n.length; o += 1) t = n.charCodeAt(o), e += r.charAt(t >>> 4 & 15) + r.charAt(15 & t);
+ return e
+ }
+
+ function r(n) {
+ return unescape(encodeURIComponent(n))
+ }
+
+ function o(n) {
+ return i(c(a(n = r(n)), 8 * n.length))
+ }
+
+ function u(n, t) {
+ return function (n, t) {
+ var r, e = a(n), o = [], u = [];
+ for (o[15] = u[15] = void 0, 16 < e.length && (e = c(e, 8 * n.length)), r = 0; r < 16; r += 1) o[r] = 909522486 ^ e[r], u[r] = 1549556828 ^ e[r];
+ return t = c(o.concat(a(t)), 512 + 8 * t.length), i(c(u.concat(t), 640))
+ }(r(n), r(t))
+ }
+
+ function t(n, t, r) {
+ return t ? r ? u(t, n) : e(u(t, n)) : r ? o(n) : e(o(n))
+ }
+
+ "function" == typeof define && define.amd ? define(function () {
+ return t
+ }) : "object" == typeof module && module.exports ? module.exports = t : n.md5 = t
+}(this);
+var md5 = this.md5;
+/**
+ 网关上线
+ 订阅:/sys/hbtgIA0SuVw9lxjB/AA:BB:CC:DD:10/c/#
+
+ 子设备注册:
+ /sys/hbtgIA0SuVw9lxjB/AA:BB:CC:DD:10/s/register
+ {
+ "id": "6",
+ "params":{
+ "productKey":"Rf4QSjbm65X45753",
+ "deviceName":"ABC12400001",
+ "model":"S1"
+ }
+ }
+ 子设备上线
+ 订阅:/sys/Rf4QSjbm65X45753/ABC12400001/c/#
+
+ 数据上报:
+ /sys/Rf4QSjbm65X45753/ABC12400001/s/event/property/post
+ {
+ "id": "6",
+ "params":{
+ "powerstate": 1
+ }
+ }
+ */
+
+var registered = {};
+
+function getPkDn(clientId) {
+ var arr = clientId.split("_");
+ return {
+ pk: arr[0],
+ dn: arr[1]
+ };
+}
+
+function register(payload) {
+ var auth = JSON.parse(payload);
+ var arr = auth.clientid.split("_");
+ if (arr.length < 3) {
+ throw new Error("incorrect clientid");
+ }
+
+ var pk = arr[0];
+ var dn = arr[1];
+ var model = arr[2];
+
+ var product = deviceBehaviour.getProductKey(pk)
+ var pwd = md5(product.getProductSecret() + auth.clientid);
+
+ if (pwd.toLocaleLowerCase() != auth.password.toLocaleLowerCase()) {
+ throw new Error("incorrect password" + "pwd->" + pwd + " productSecret->" + product.productSecret);
+ }
+ return {
+ type: "register",
+ data: {
+ productKey: pk,
+ deviceName: dn,
+ model: model
+ }
+ };
+}
+
+function subRegister(topic, parent, payload) {
+ var params = payload.params;
+ var reply =
+ {
+ productKey: parent.productKey,
+ deviceName: parent.deviceName,
+ mid: "0",
+ content: {
+ topic: topic.replace("/s/", "/c/") + "_reply",
+ payload: JSON.stringify({
+ id: "0",
+ code: 0,
+ data: {
+ "productKey": params.productKey,
+ "deviceName": params.deviceName
+ }
+ })
+ }
+ };
+
+ return {
+ type: "register",
+ data: {
+ productKey: parent.productKey,
+ deviceName: parent.deviceName,
+ subDevices: [{
+ productKey: params.productKey,
+ deviceName: params.deviceName,
+ model: params.model
+ }]
+ },
+ action: {
+ type: "ack",
+ content: JSON.stringify(reply)
+ }
+ };
+}
+
+function deviceStateChange(head, clientId, state) {
+ var topic = head.topic;
+ var device = getPkDn(clientId);
+
+ var arr = topic.split('/');
+ if (arr.length < 6) {
+ throw new Error("incorrect topic")
+ }
+
+ var pk = arr[2];
+ var dn = arr[3];
+ return {
+ type: "state",
+ data: {
+ productKey: pk,
+ deviceName: dn,
+ state: state,
+ parent: {
+ productKey: device.pk,
+ deviceName: device.dn,
+ }
+ }
+ }
+}
+
+function disconnect(clientId) {
+ var device = getPkDn(clientId);
+ return {
+ type: "state",
+ data: {
+ productKey: device.pk,
+ deviceName: device.dn,
+ state: "offline"
+ }
+ }
+}
+
+function ota(head, payload) {
+ payload = JSON.parse(payload);
+ var topic = head.topic;
+ var arr = topic.split('/');
+ if (arr.length < 6) {
+ throw new Error("incorrect topic")
+ }
+
+ var pk = arr[2];
+ var dn = arr[3];
+ return {
+ type: "ota",
+ data: {
+ productKey: pk,
+ deviceName: dn,
+ mid: payload.id,
+ content: {
+ topic: topic,
+ payload: payload
+ }
+ }
+ }
+}
+
+//必须提供onReceive方法
+this.onReceive = function (head, type, payload) {
+ if (type == 'auth') {
+ return register(payload);
+ }
+
+ if (type == 'subscribe') {
+ return deviceStateChange(head, payload, 'online');
+ }
+
+ if (type == 'unsubscribe') {
+ return deviceStateChange(head, payload, 'offline');
+ }
+
+ if (type == 'disconnect') {
+ return disconnect(payload);
+ }
+
+ if (type == 'ota') {
+ return ota(head, payload);
+ }
+
+ var topic = head['topic'];
+ if (!topic) {
+ throw new Error("topic is blank")
+ }
+
+ var arr = topic.split('/');
+ if (arr.length < 6) {
+ throw new Error("incorrect topic")
+ }
+ var pk = arr[2];
+ var dn = arr[3];
+ payload = JSON.parse(payload);
+
+ //子设备注册
+ if (topic.endsWith('/register')) {
+ return subRegister(topic, {productKey: pk, deviceName: dn}, payload);
+ }
+
+ //数据上报
+ var reply =
+ {
+ productKey: pk,
+ deviceName: dn,
+ mid: payload.id,
+ content: {
+ topic: topic.replace("/s/", "/c/") + "_reply",
+ payload: JSON.stringify({
+ id: payload.id,
+ method: payload.method + "_reply",
+ code: 0,
+ })
+ }
+ };
+
+ var action = {};
+ if (!topic.endsWith("_reply")) {
+ //需要回复的消息
+ action = {
+ type: "ack",
+ content: JSON.stringify(reply)
+ }
+ }
+
+ return {
+ type: "report",
+ data: {
+ productKey: pk,
+ deviceName: dn,
+ mid: payload.id,
+ content: {
+ topic: topic,
+ payload: payload
+ }
+ },
+ action: action
+ }
+}
+
+this.onRegistered = function (regInfo, result) {
+}
\ No newline at end of file
diff --git a/iot-components/iot-nb-component/src/main/resources/component.spi b/iot-components/iot-nb-component/src/main/resources/component.spi
new file mode 100644
index 00000000..74979ba0
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/resources/component.spi
@@ -0,0 +1 @@
+cc.iotkit.comp.nb.NBDeviceComponent
\ No newline at end of file
diff --git a/iot-components/iot-nb-component/src/main/resources/convert.js b/iot-components/iot-nb-component/src/main/resources/convert.js
new file mode 100644
index 00000000..8439bf40
--- /dev/null
+++ b/iot-components/iot-nb-component/src/main/resources/convert.js
@@ -0,0 +1,601 @@
+var mid = 1;
+
+function getMid() {
+ mid++;
+ if (mid > 10000) {
+ mid = 1;
+ }
+ return mid + "";
+}
+//上行数据
+this.decode = function (msg) {
+ var content = msg.content;
+ var topic = content.topic;
+ var bytes = CRC.strToByte(content.payload);
+
+ if (topic.endsWith("/thing/model/up_raw")) {
+ var byteData = ab2hex(bytes)
+ var data = arrayGroup(byteData, 2);
+ var params = {};
+ var uint8Array = new Uint8Array(bytes.length);
+ for (var i = 0; i < bytes.length; i++) {
+ uint8Array[i] = bytes[i] & 0xff;
+ }
+ if (data[1] == '03') {
+ if (byteData.length == 22 ) {
+ params['Switch'] = Number(parseInt(data[3] + data[4], 16))
+ params['HandMode'] = Number(parseInt(data[5] + data[6], 16))+1
+ params['TempGear'] = Number(data[7].slice(1,2))+1
+ params['RatioGear'] = Number(data[7].slice(0,1))+1
+ params['PressureGear'] = data[8].slice(0,1) != 'f' ? Number(data[8].slice(0,1))+1 : 0
+ params['SpeedGear'] = data[8].slice(1,2) != 'f' ? Number(data[8].slice(1,2))+1 : 0
+
+ } else if (byteData.length == 126) {
+ params['Pressure'] = Number((parseInt(data[3] + data[4], 16) / 100).toFixed(1))
+ params['EmtyRunPressure'] = Number((parseInt(data[5] + data[6], 16) / 100).toFixed(1))
+ params['StartPressure'] = Number((parseInt(data[7] + data[8], 16) / 100).toFixed(1))
+ params['WorkMode'] = Number((parseInt(data[9] + data[10], 16)).toFixed(0))
+ params['ClearIceEn'] = Number((parseInt(data[11] + data[12], 16)).toFixed(0))
+ params['WaterT'] = Number((parseInt(data[13] + data[14], 16)).toFixed(0))
+ params['WaterTReset'] = Number((parseInt(data[15] + data[16], 16)).toFixed(0))
+ params['SensorMode'] = Number((parseInt(data[17] + data[18], 16)).toFixed(0))
+ params['SensorGroup'] = Number((parseInt(data[19] + data[20], 16)).toFixed(0))
+ params['ElectronicTMax'] = Number(parseInt(data[59], 16))
+ params['ElectronicTMaxReset'] = Number(parseInt(data[60], 16))
+ } else if (byteData.length == 26) {
+ params['ActiveTime'] = String(parseInt(data[3], 16))+'-'+String(parseInt(data[4], 16))+'-'+String(parseInt(data[5], 16))
+ } else if (byteData.length == 190) {
+ params['Pressure'] = Number((parseInt(data[3] + data[4], 16) / 100).toFixed(1))
+ params['EmtyRunPressure'] = Number((parseInt(data[5] + data[6], 16) / 100).toFixed(1))
+ params['StartPressure'] = Number((parseInt(data[7] + data[8], 16) / 100).toFixed(1))
+ params['WorkMode'] = Number((parseInt(data[9] + data[10], 16)).toFixed(0))
+ params['ClearIceEn'] = Number((parseInt(data[11] + data[12], 16)).toFixed(0))
+ params['WaterT'] = Number((parseInt(data[13] + data[14], 16)).toFixed(0))
+ params['WaterTReset'] = Number((parseInt(data[15] + data[16], 16)).toFixed(0))
+ params['SensorMode'] = Number((parseInt(data[17] + data[18], 16)).toFixed(0))
+ params['SensorGroup'] = Number((parseInt(data[19] + data[20], 16)).toFixed(0))
+ params['ElectronicTMax'] = Number(parseInt(data[59], 16))
+ params['ElectronicTMaxReset'] = Number(parseInt(data[60], 16))
+ params['SceneMode'] = Number((parseInt(data[63] + data[64], 16)).toFixed(0))
+ params['EnergyModeTime'] = Number((parseInt(data[65] + data[66], 16)).toFixed(0))
+ params['WaterTime'] = String(Number((parseInt(data[68].substring(0, 1), 16)).toFixed(0))) + ',' + String(Number((parseInt(data[68].substring(1), 16)).toFixed(0)))
+ params['HotWaterTime'] = Number((parseInt(data[69] + data[70], 16)).toFixed(0))
+ params['TempSet'] = String(Number((parseInt(data[71], 16)).toFixed(0)))+','+ String(Number((parseInt(data[72], 16)).toFixed(0)))
+ params['TimeModeSet'] = parseInt(data[73] + data[74], 16)+'-'+parseInt(data[75] + data[76], 16)+','+ parseInt(data[77] + data[78], 16)+'-'+parseInt(data[79] + data[80], 16)+','+ parseInt(data[81] + data[82], 16)+'-'+parseInt(data[83] + data[84], 16)+','+ parseInt(data[85] + data[86], 16)+'-'+parseInt(data[87] + data[88], 16)+','+ parseInt(data[89] + data[90], 16)+'-'+parseInt(data[91] + data[92], 16)
+
+ } else if (byteData.length == 94) {
+ let timeStamp = parseInt(String(data[41])+String(data[42])+String(data[43])+String(data[44]),16)
+ let time = timestampToTime(timeStamp)
+ params['ActiveTime'] = String(parseInt(data[3], 16))+'-'+String(parseInt(data[4], 16))+'-'+String(parseInt(data[5], 16))
+ params['Time'] = time + '-'+ timeStamp
+
+ }
+ } else if (data[1] == '04') {
+ if (byteData.length == 54) {
+ params['ErrorMsg'] = Number(parseInt(data[3]+data[4], 16))
+ params['Voltage'] = Number(parseInt(data[5]+data[6], 16))
+ params['Electric'] = Number(parseInt(data[7]+data[8], 16))/10
+ params['Power'] = Number(parseInt(data[9]+data[10], 16))
+ params['Speed'] = Number(parseInt(data[11]+data[12], 16))
+ params['CurrentPressure1'] = data[13] != 'ff' ? Number((parseInt(data[13]+data[14], 16)/100).toFixed(1)) : 0
+ params['CurrentPressure2'] = data[15] != 'ff' ? Number((parseInt(data[15]+data[16], 16)/100).toFixed(1)) : 0
+ params['IpmTemperature'] = Number(parseInt(data[17]+data[18], 16))-55
+ params['MotorTemperature'] = Number(parseInt(data[19]+data[20], 16))-55
+ params['WaterTemperature'] = Number(parseInt(data[21]+data[22], 16))-55
+ let warnArray = reverseStr(hex2bin(data[23] + data[24]))
+ let warnInfo = ""
+ for (let i = 0; i < warnArray.length; i++) {
+ if (warnArray[i] === "1") {
+ if (warnInfo === "") {
+ warnInfo = DEVICE_ERROR[i]
+ } else {
+ warnInfo += `、${DEVICE_ERROR[i]}`
+ }
+ }
+ }
+
+ params['WarnInfo'] = warnInfo
+
+ } else if (byteData.length == 18) {
+ if (String(byteData) == '130404000800004847') {
+ params['query'] = String(byteData)
+ } else if (String(byteData).slice(9,10) == 3) {
+ params['query'] = String(byteData)
+ } else if (String(byteData).slice(9,10) == 4) {
+ params['query'] = String(byteData)
+ }
+ } else if (byteData.length == 30) {
+ params['McuVersion'] = String(Number(parseInt(data[3].slice(1,2), 16)))+'.'+String(Number(parseInt(data[4].slice(0,1), 16)))+'.'+String(Number(parseInt(data[4].slice(1,2), 16)))
+ params['McuStatus'] = Number(parseInt(data[3].slice(0,1), 16))
+ params['Scene'] = Number(parseInt(data[9]+data[10], 16))
+ params['Model'] = String(Number(parseInt(data[11]+data[12], 16)))+'W'
+
+ } else if (byteData.length == 66) {
+ params['ErrorMsg'] = Number(parseInt(data[3]+data[4], 16))
+ params['Voltage'] = Number(parseInt(data[5]+data[6], 16))
+ params['Electric'] = Number(parseInt(data[7]+data[8], 16))/10
+ params['Power'] = Number(parseInt(data[9]+data[10], 16))
+ params['Speed'] = Number(parseInt(data[11]+data[12], 16))
+ params['CurrentPressure1'] = data[13] != 'ff' ? Number((parseInt(data[13]+data[14], 16)/100).toFixed(1)) : 0
+ params['CurrentPressure2'] = data[15] != 'ff' ? Number((parseInt(data[15]+data[16], 16)/100).toFixed(1)) : 0
+ params['IpmTemperature'] = Number(parseInt(data[17]+data[18], 16))-55
+ params['MotorTemperature'] = Number(parseInt(data[19]+data[20], 16))-55
+ params['WaterTemperature'] = Number(parseInt(data[21]+data[22], 16))-55
+ let warnArray = reverseStr(hex2bin(data[23] + data[24]))
+ let warnInfo = ""
+ for (let i = 0; i < warnArray.length; i++) {
+ if (warnArray[i] === "1") {
+ if (warnInfo === "") {
+ warnInfo = DEVICE_ERROR[i]
+ } else {
+ warnInfo += `、${DEVICE_ERROR[i]}`
+ }
+ }
+ }
+ params['CountDown'] = Number(parseInt(data[25]+data[26], 16))
+ params['WarnInfo'] = warnInfo
+ params['Enabled'] = Number(parseInt(data[29]+data[30], 16))
+
+ }
+ }
+ params['History'] = byteData
+ //属性上报
+ return {
+ mid: msg.mid,
+ productKey: msg.productKey,
+ deviceName: msg.deviceName,
+ type: "property",
+ identifier: "report", //属性上报
+ occur: new Date().getTime(), //时间戳,设备上的事件或数据产生的本地时间
+ time: new Date().getTime(), //时间戳,消息上报时间
+ data: params,
+ };
+ } else if (topic.indexOf("/event/") > 0) {
+ //事件上报
+ } else if (topic.endsWith("/service/property/set_reply")) {
+ //属性设置回复
+ } else if (topic.endsWith("/config/set_reply")) {
+ //设备配置设置回复
+ } else if (topic.endsWith("/config/get")) {
+ //设备配置获取
+ } else if (topic.endsWith("_reply")) {
+ //服务回复
+ }
+ return null;
+};
+
+//下行数据
+this.encode = function (service, device) {
+ var deviceMid = getMid();
+ var method = "thing.service.";
+ var topic = "/sys/" + service.productKey + "/" + service.deviceName + "/thing/model/down_raw";
+ var params = {};
+
+ var type = service.type;
+ var identifier = service.identifier;
+ if (type == "property" && identifier == "get") {
+ var listParams = []
+ for (var p in service.params) {
+ listParams.push(service.params[p]);
+ }
+ return {
+ productKey: service.productKey,
+ deviceName: service.deviceName,
+ mid: deviceMid,
+ content: {
+ topic: topic,
+ payload: JSON.stringify({
+ id: deviceMid,
+ method: method += "property." + identifier,
+ params: listParams
+ })
+ }
+ }
+ } else if (type == "property" && identifier == "set") {
+ for (var p in service.params) {
+ params[p] = service.params[p];
+ }
+ //站地址
+ if (paramsArr.includes('query')) {
+ let queryHexData = arrayGroup(params['query'],2)
+ let queryData = []
+ queryHexData.map(function(value) {
+ queryData.push(parseInt(value,16))
+ })
+ payloadArray = queryData
+ } else {
+ const stationAddress = "49";
+ totalArray.push(stationAddress)
+ //功能码
+ var functionCode = "";
+ // 寄存器数量
+ var register = ''
+ var baseVal = ''
+ //起始地址
+ var startAddressCode = '';
+ if (paramsArr.length == 1) {
+ functionCode = '06'
+ totalArray.push(functionCode)
+ } else {
+ functionCode = '10'
+ paramsArr = ['Pressure', 'EmtyRunPressure', 'StartPressure', 'WorkMode', 'ClearIceEn', 'WaterT', 'WaterTReset']
+ totalArray.push(parseInt(Number(functionCode),16))
+ }
+ if (paramsArr.includes('Switch')) {
+ startAddressCode = 300
+ totalArray.push('01','44')
+ } else if (paramsArr.includes('Pressure')) {
+ startAddressCode = 400
+ totalArray.push('01','144')
+ register = '00' + add0(String(paramsArr.length),2) + add0(String((paramsArr.length*2).toString(16)),2)
+ totalArray.push('00', add0(paramsArr.length,2), add0(String((paramsArr.length)*2),2))
+ }
+ if (functionCode == 6) {
+ paramsArr.forEach(function(value){
+ totalArray.push('00',params[value])
+ itemArray = pad(params[value].toString(16),4)
+ deviceArray.push(itemArray[0],itemArray[1])
+ })
+ } else if (functionCode == 10) {
+ paramsArr.forEach(function(value, index){
+ if (index < 3) {
+ // if (params[value]*100 < 256) {
+ // totalItemArray = ['00', String((params[value]*100).toFixed(0))]
+ // itemArray = pad(String((params[value]*100).toString(16)),4)
+ // totalItemArray = pad(String((params[value]*100).toFixed(0)),4)
+ // } else {
+ // outFFIndex = ((Number(params[value]*100))/256).toFixed(0)
+ // totalItemArray = [outFFIndex, params[value]*100-outFFIndex*256]
+ // }
+
+ itemArray = pad(Number((params[value]*100).toFixed(0)).toString(16),4)
+ totalItemArray = pad(String((params[value]*100).toFixed(0)),4)
+ } else {
+ itemArray = pad(String(params[value].toString(16)),4)
+ totalItemArray = pad(String(params[value]),4)
+ }
+ // totalArray.push(params[value])
+ // totalArray.push(totalItemArray[0],totalItemArray[1])
+ totalArray.push(parseInt(itemArray[0],16),parseInt(itemArray[1],16))
+ // deviceArray.push(parseInt(itemArray[0],16),parseInt(itemArray[1],16))
+ deviceArray.push(itemArray[0],itemArray[1])
+
+ })
+ }
+
+ var startAddressHex = pad(startAddressCode.toString(16), 4);
+ baseVal = String(stationAddress) + String(functionCode) + String(startAddressHex[0]) + String(startAddressHex[1])
+ if (functionCode != 6) {
+ baseVal += register
+ }
+ deviceArray.forEach(function(item) {
+ baseVal += item
+ })
+ // baseVal += '00070e0064000a00640001000100370037'
+ var Crc = CRC.ToModbusCRC16(baseVal)
+ var crcArray = arrayGroup(Crc,2)
+ totalArray.push(parseInt(crcArray[0],16),parseInt(crcArray[1],16))
+ payloadArray = totalArray;
+ }
+ // var params = json['params'];
+ // var prop_float = params['prop_float'];
+ // var prop_int16 = params['prop_int16'];
+ // var prop_bool = params['prop_bool'];
+ // //按照自定义协议格式拼接 rawData。
+ // payloadArray = payloadArray.concat(buffer_uint8(COMMAND_SET)); //command字段。
+ // payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); //ALink JSON格式 'id'。
+ // payloadArray = payloadArray.concat(buffer_int16(prop_int16)); //属性'prop_int16'的值。
+ // payloadArray = payloadArray.concat(buffer_uint8(prop_bool)); //属性'prop_bool'的值。
+ // payloadArray = payloadArray.concat(buffer_float32(prop_float)); //属性'prop_float'的值。
+
+ }else if (method == 'thing.event.property.post') { //设备上报数据返回结果,如果不需要回复,可以去除该内容
+ var code = json['code'];
+ payloadArray = payloadArray.concat(buffer_uint8(COMMAND_REPORT_REPLY)); //command字段
+ payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ALink JSON格式 'id'
+ payloadArray = payloadArray.concat(buffer_uint8(code));
+ } else { //未知命令,对于有些命令不做处理
+ var code = json['code'];
+ payloadArray = payloadArray.concat(buffer_uint8(COMMAD_UNKOWN)); //command字段
+ payloadArray = payloadArray.concat(buffer_int32(parseInt(id))); // ALink JSON格式 'id'
+ payloadArray = payloadArray.concat(buffer_uint8(code));
+ }
+ return {
+ productKey: service.productKey,
+ deviceName: service.deviceName,
+ mid: deviceMid,
+ content: {
+ topic: topic,
+ payload: JSON.stringify({
+ id: deviceMid,
+ method: method += "property." + identifier,
+ params: payloadArray
+ })
+ }
+ }
+};
+
+
+const DEVICE_ERROR = [
+ "压力传感器故障", //0
+ "高温限功率", //1
+ "渗漏", // 2
+ "电机温度传感器故障", //3
+ "水温传感器故障", //4
+ "保留", //5
+ "保留", //6
+ "保留", //7
+ "保留", //8
+ "保留", //9
+ "保留", //10
+ "保留", //11
+ "保留", //12
+ "保留", //13
+ "保留", //14
+ "保留", // 15
+ "保留", //16
+]
+function buffer_uint8(value) {
+ var uint8Array = new Uint8Array(1);
+ var dv = new DataView(uint8Array.buffer, 0);
+ dv.setUint8(0, value);
+ return [].slice.call(uint8Array);
+}
+function buffer_int16(value) {
+ var uint8Array = new Uint8Array(2);
+ var dv = new DataView(uint8Array.buffer, 0);
+ dv.setInt16(0, value);
+ return [].slice.call(uint8Array);
+}
+function buffer_int32(value) {
+ var uint8Array = new Uint8Array(4);
+ var dv = new DataView(uint8Array.buffer, 0);
+ dv.setInt32(0, value);
+ return [].slice.call(uint8Array);
+}
+function buffer_float32(value) {
+ var uint8Array = new Uint8Array(4);
+ var dv = new DataView(uint8Array.buffer, 0);
+ dv.setFloat32(0, value);
+ return [].slice.call(uint8Array);
+}
+
+function ab2hex(buffer) {
+ var hexArr = Array.prototype.map.call(
+ new Uint8Array(buffer),
+ function (bit) {
+ return ('00' + bit.toString(16)).slice(-2)
+ }
+ )
+ return hexArr.join('');
+}
+
+function arrayGroup(ss, step) {
+ var r = [];
+
+ function doGroup(s) {
+ if (!s) return;
+ r.push(s.substr(0, step));
+ s = s.substr(step);
+ doGroup(s);
+ }
+ doGroup(ss);
+ return r;
+}
+
+var CRC = {};
+
+CRC._auchCRCHi = [
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40,
+ 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0, 0x80, 0x41, 0x00, 0xC1,
+ 0x81, 0x40, 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41,
+ 0x00, 0xC1, 0x81, 0x40, 0x01, 0xC0, 0x80, 0x41, 0x01, 0xC0,
+ 0x80, 0x41, 0x00, 0xC1, 0x81, 0x40
+];
+CRC._auchCRCLo = [
+ 0x00, 0xC0, 0xC1, 0x01, 0xC3, 0x03, 0x02, 0xC2, 0xC6, 0x06,
+ 0x07, 0xC7, 0x05, 0xC5, 0xC4, 0x04, 0xCC, 0x0C, 0x0D, 0xCD,
+ 0x0F, 0xCF, 0xCE, 0x0E, 0x0A, 0xCA, 0xCB, 0x0B, 0xC9, 0x09,
+ 0x08, 0xC8, 0xD8, 0x18, 0x19, 0xD9, 0x1B, 0xDB, 0xDA, 0x1A,
+ 0x1E, 0xDE, 0xDF, 0x1F, 0xDD, 0x1D, 0x1C, 0xDC, 0x14, 0xD4,
+ 0xD5, 0x15, 0xD7, 0x17, 0x16, 0xD6, 0xD2, 0x12, 0x13, 0xD3,
+ 0x11, 0xD1, 0xD0, 0x10, 0xF0, 0x30, 0x31, 0xF1, 0x33, 0xF3,
+ 0xF2, 0x32, 0x36, 0xF6, 0xF7, 0x37, 0xF5, 0x35, 0x34, 0xF4,
+ 0x3C, 0xFC, 0xFD, 0x3D, 0xFF, 0x3F, 0x3E, 0xFE, 0xFA, 0x3A,
+ 0x3B, 0xFB, 0x39, 0xF9, 0xF8, 0x38, 0x28, 0xE8, 0xE9, 0x29,
+ 0xEB, 0x2B, 0x2A, 0xEA, 0xEE, 0x2E, 0x2F, 0xEF, 0x2D, 0xED,
+ 0xEC, 0x2C, 0xE4, 0x24, 0x25, 0xE5, 0x27, 0xE7, 0xE6, 0x26,
+ 0x22, 0xE2, 0xE3, 0x23, 0xE1, 0x21, 0x20, 0xE0, 0xA0, 0x60,
+ 0x61, 0xA1, 0x63, 0xA3, 0xA2, 0x62, 0x66, 0xA6, 0xA7, 0x67,
+ 0xA5, 0x65, 0x64, 0xA4, 0x6C, 0xAC, 0xAD, 0x6D, 0xAF, 0x6F,
+ 0x6E, 0xAE, 0xAA, 0x6A, 0x6B, 0xAB, 0x69, 0xA9, 0xA8, 0x68,
+ 0x78, 0xB8, 0xB9, 0x79, 0xBB, 0x7B, 0x7A, 0xBA, 0xBE, 0x7E,
+ 0x7F, 0xBF, 0x7D, 0xBD, 0xBC, 0x7C, 0xB4, 0x74, 0x75, 0xB5,
+ 0x77, 0xB7, 0xB6, 0x76, 0x72, 0xB2, 0xB3, 0x73, 0xB1, 0x71,
+ 0x70, 0xB0, 0x50, 0x90, 0x91, 0x51, 0x93, 0x53, 0x52, 0x92,
+ 0x96, 0x56, 0x57, 0x97, 0x55, 0x95, 0x94, 0x54, 0x9C, 0x5C,
+ 0x5D, 0x9D, 0x5F, 0x9F, 0x9E, 0x5E, 0x5A, 0x9A, 0x9B, 0x5B,
+ 0x99, 0x59, 0x58, 0x98, 0x88, 0x48, 0x49, 0x89, 0x4B, 0x8B,
+ 0x8A, 0x4A, 0x4E, 0x8E, 0x8F, 0x4F, 0x8D, 0x4D, 0x4C, 0x8C,
+ 0x44, 0x84, 0x85, 0x45, 0x87, 0x47, 0x46, 0x86, 0x82, 0x42,
+ 0x43, 0x83, 0x41, 0x81, 0x80, 0x40
+];
+
+CRC.CRC16 = function (buffer) {
+ var hi = 0xff;
+ var lo = 0xff;
+ for (var i = 0; i < buffer.length; i++) {
+ var idx = hi ^ buffer[i];
+ hi = (lo ^ CRC._auchCRCHi[idx]);
+ lo = CRC._auchCRCLo[idx];
+ }
+ return CRC.padLeft((hi << 8 | lo).toString(16).toUpperCase(), 4, '0');
+};
+
+CRC.isArray = function (arr) {
+ return Object.prototype.toString.call(arr) === '[object Array]';
+};
+
+CRC.ToCRC16 = function (str) {
+ return CRC.CRC16(CRC.isArray(str) ? str : CRC.strToByte(str));
+};
+
+CRC.ToModbusCRC16 = function (str) {
+ return CRC.CRC16(CRC.isArray(str) ? str : CRC.strToHex(str));
+};
+
+CRC.strToByte = function (str) {
+ var tmp = str.split(''),
+ arr = [];
+ for (var i = 0, c = tmp.length; i < c; i++) {
+ var j = encodeURI(tmp[i]);
+ if (j.length == 1) {
+ arr.push(j.charCodeAt());
+ } else {
+ var b = j.split('%');
+ for (var m = 1; m < b.length; m++) {
+ arr.push(parseInt('0x' + b[m]));
+ }
+ }
+ }
+ return arr;
+};
+
+CRC.convertChinese = function (str) {
+ var tmp = str.split(''),
+ arr = [];
+ for (var i = 0, c = tmp.length; i < c; i++) {
+ var s = tmp[i].charCodeAt();
+ if (s <= 0 || s >= 127) {
+ arr.push(s.toString(16));
+ } else {
+ arr.push(tmp[i]);
+ }
+ }
+ return arr;
+};
+
+CRC.filterChinese = function (str) {
+ var tmp = str.split(''),
+ arr = [];
+ for (var i = 0, c = tmp.length; i < c; i++) {
+ var s = tmp[i].charCodeAt();
+ if (s > 0 && s < 127) {
+ arr.push(tmp[i]);
+ }
+ }
+ return arr;
+};
+
+CRC.strToHex = function (hex, isFilterChinese) {
+ hex = isFilterChinese ? CRC.filterChinese(hex).join('') : CRC.convertChinese(hex).join('');
+
+ //清除所有空格
+ hex = hex.replace(/\s/g, "");
+ //若字符个数为奇数,补一个空格
+ hex += hex.length % 2 != 0 ? " " : "";
+
+ var c = hex.length / 2,
+ arr = [];
+ for (var i = 0; i < c; i++) {
+ arr.push(parseInt(hex.substr(i * 2, 2), 16));
+ }
+ return arr;
+};
+
+CRC.padLeft = function (s, w, pc) {
+ if (pc == undefined) {
+ pc = '0';
+ }
+ for (var i = 0, c = w - s.length; i < c; i++) {
+ s = pc + s;
+ }
+ return s;
+};
+
+function str2ab(str) {
+ var buffer = new ArrayBuffer(str.length / 2); // 2 bytes for each char
+ var dataView = new DataView(buffer);
+ var str = str.split("")
+ var n = 0;
+ for (var i = 0; i < str.length; i = i + 2) {
+ dataView.setUint8(n, `0x${str[i]}${str[i+1]}`)
+ n++;
+ }
+ return buffer;
+}
+
+function pad(num, n, flag = false) {
+ var len = num.toString().length;
+ while (len < n) {
+ num = "0" + num;
+ len++;
+ }
+
+ const arr = arrayGroup(num, 2)
+ //高低位互换
+ if (arr[1] == '00') {
+ flag = true
+ }
+ return flag ? [arr[1], arr[0]] : [arr[0], arr[1]];
+}
+
+function add0(num,n) {
+ let len = num.length
+ while(len < n){
+ num = '0'+num
+ len++
+ }
+ if (len > n) {
+ num = String(num).slice(1)
+ }
+ //console.log(num)
+ return num
+}
+
+function hex2bin(hex, len) {
+ var bin = parseInt(hex, 16).toString(2);
+ var l = bin.length;
+ for (var i = 0; i < len - l; i++) {
+ bin = "0" + bin;
+ }
+ return bin;
+}
+
+//翻转字符串并切割成数组
+function reverseStr(str) {
+ var arr = str.split("");
+ arr.reverse();
+ return arr;
+}
+
+function timestampToTime(timestamp) {
+ var date = new Date(timestamp * 1000);//时间戳为10位需*1000,时间戳为13位的话不需乘1000
+ var Y = date.getFullYear() + '-';
+ var M = (date.getMonth()+1 < 10 ? '0'+(date.getMonth()+1) : date.getMonth()+1) + '-';
+ var D = date.getDate() + ' ';
+ var h = date.getHours() + ':';
+ var m = date.getMinutes() + ':';
+ var s = date.getSeconds();
+ return Y+M+D+h+m+s;
+}
\ No newline at end of file
diff --git a/iot-components/pom.xml b/iot-components/pom.xml
index 92cdb595..af35f4d6 100755
--- a/iot-components/pom.xml
+++ b/iot-components/pom.xml
@@ -21,7 +21,8 @@
iot-component-tcp
iot-DLT645-component
iot-websocket-component
-
+ iot-nb-component
+
\ No newline at end of file
diff --git a/iot-starter/pom.xml b/iot-starter/pom.xml
index adb3b9d0..dfe8ac2a 100644
--- a/iot-starter/pom.xml
+++ b/iot-starter/pom.xml
@@ -211,7 +211,6 @@
-
diff --git a/pom.xml b/pom.xml
index 496502be..5fe18279 100755
--- a/pom.xml
+++ b/pom.xml
@@ -509,6 +509,12 @@
${project.version}
+
+ cc.iotkit
+ iot-nb-component
+ ${project.version}
+
+
cc.iotkit
iot-manager