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} + ${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