[{"id":"f96c5c3963c0cdaf","type":"tab","label":"次卧纱帘","disabled":false,"info":"","env":[]},{"id":"fba64588a0506317","type":"tcp out","z":"f96c5c3963c0cdaf","name":"TCP发送","host":"10.10.10.46","port":"23","beserver":"client","base64":false,"end":false,"tls":"","x":960,"y":200,"wires":[]},{"id":"e9ab3601f48277a0","type":"function","z":"f96c5c3963c0cdaf","name":"拉开窗帘","func":"// 协议参数\nconst ID_L = 0xfe; // 设备地址低位\nconst ID_H = 0xfe; // 设备地址高位\nconst FUNC_READ = 0x03; // 功能码\nconst CMD = 0x01; // 指令-开启\n\n\n// 构造数据帧(不含CRC)\nlet frame = Buffer.from([\n 0x55, // 起始码\n ID_L, ID_H, // 设备地址\n FUNC_READ, // 功能码\n CMD, // 指令\n]);\n\n// 计算CRC16-MODBUS(直接调用CRC节点函数)\n//const crc = global.get('crc').CRC16Modbus(frame);\n//const crc = global.get('crc').modbus-crc16(frame);\n//msg.payload = Buffer.concat([frame, crc]);\nmsg.payload = Buffer.concat([frame]);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":360,"wires":[["60534442d46a396a"]]},{"id":"60534442d46a396a","type":"modbus-crc16","z":"f96c5c3963c0cdaf","name":"","x":740,"y":360,"wires":[["074421ed8f901b40","fba64588a0506317"]]},{"id":"33cc085ae43048aa","type":"inject","z":"f96c5c3963c0cdaf","name":"手动触发","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":360,"wires":[["e9ab3601f48277a0"]]},{"id":"074421ed8f901b40","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":970,"y":360,"wires":[]},{"id":"eed75601b8fa1756","type":"function","z":"f96c5c3963c0cdaf","name":"关闭窗帘","func":"// 协议参数\nconst ID_L = 0xfe; // 设备地址低位\nconst ID_H = 0xfe; // 设备地址高位\nconst FUNC_READ = 0x03; // 功能码\nconst CMD = 0x02; // 指令-关闭\n\n\n// 构造数据帧(不含CRC)\nlet frame = Buffer.from([\n 0x55, // 起始码\n ID_L, ID_H, // 设备地址\n FUNC_READ, // 功能码\n CMD, // 指令\n]);\n\n// 计算CRC16-MODBUS(直接调用CRC节点函数)\n//const crc = global.get('crc').CRC16Modbus(frame);\n//const crc = global.get('crc').modbus-crc16(frame);\n//msg.payload = Buffer.concat([frame, crc]);\nmsg.payload = Buffer.concat([frame]);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":540,"y":440,"wires":[["98f9715a3292a940"]]},{"id":"98f9715a3292a940","type":"modbus-crc16","z":"f96c5c3963c0cdaf","name":"","x":740,"y":440,"wires":[["d734f8a9fd635ae9","fba64588a0506317"]]},{"id":"201fd1a0178b6e5d","type":"inject","z":"f96c5c3963c0cdaf","name":"手动触发","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":440,"wires":[["eed75601b8fa1756"]]},{"id":"d734f8a9fd635ae9","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":970,"y":440,"wires":[]},{"id":"086d0f1ced0a7329","type":"function","z":"f96c5c3963c0cdaf","name":"停止","func":"// 协议参数\nconst ID_L = 0xfe; // 设备地址低位\nconst ID_H = 0xfe; // 设备地址高位\nconst FUNC_READ = 0x03; // 功能码\nconst CMD = 0x03; // 指令-停止\n\n\n// 构造数据帧(不含CRC)\nlet frame = Buffer.from([\n 0x55, // 起始码\n ID_L, ID_H, // 设备地址\n FUNC_READ, // 功能码\n CMD, // 指令\n]);\n\n// 计算CRC16-MODBUS(直接调用CRC节点函数)\n//const crc = global.get('crc').CRC16Modbus(frame);\n//const crc = global.get('crc').modbus-crc16(frame);\n//msg.payload = Buffer.concat([frame, crc]);\nmsg.payload = Buffer.concat([frame]);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":530,"y":520,"wires":[["6bb1984fccf0ab49"]]},{"id":"6bb1984fccf0ab49","type":"modbus-crc16","z":"f96c5c3963c0cdaf","name":"","x":740,"y":520,"wires":[["8e710240c5c50ccd","fba64588a0506317"]]},{"id":"d89889643fcb70ec","type":"inject","z":"f96c5c3963c0cdaf","name":"手动触发","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":520,"wires":[["086d0f1ced0a7329"]]},{"id":"8e710240c5c50ccd","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"payload","targetType":"msg","statusVal":"payload","statusType":"auto","x":970,"y":520,"wires":[]},{"id":"f4284372f74ded93","type":"function","z":"f96c5c3963c0cdaf","name":"设定百分比","func":"// 协议参数\nconst ID_L = 0xfe; // 设备地址低位\nconst ID_H = 0xfe; // 设备地址高位\nconst FUNC_READ = 0x03; // 功能码\nconst CMD = 0x04; // 指令-百分比\n//const DAT = 50; // 百分比数值-0-100\n\nvar DAT = parseInt(msg.NUM);\n// 构造数据帧(不含CRC)\nlet frame = Buffer.from([\n 0x55, // 起始码\n ID_L, ID_H, // 设备地址\n FUNC_READ, // 功能码\n CMD, // 指令\n DAT\n]);\n\n// 计算CRC16-MODBUS(直接调用CRC节点函数)\n//const crc = global.get('crc').CRC16Modbus(frame);\n//const crc = global.get('crc').modbus-crc16(frame);\n//msg.payload = Buffer.concat([frame, crc]);\nmsg.payload = Buffer.concat([frame]);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":600,"wires":[["d5e81ab2955555fb"]]},{"id":"d5e81ab2955555fb","type":"modbus-crc16","z":"f96c5c3963c0cdaf","name":"","x":740,"y":600,"wires":[["cffa0ebbe25e2650","fba64588a0506317"]]},{"id":"42df406370439923","type":"inject","z":"f96c5c3963c0cdaf","name":"手动触发","props":[],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":380,"y":600,"wires":[["f4284372f74ded93"]]},{"id":"cffa0ebbe25e2650","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":950,"y":600,"wires":[]},{"id":"83a4502ac8098751","type":"inject","z":"f96c5c3963c0cdaf","name":"自动读取","props":[],"repeat":"2","crontab":"","once":false,"onceDelay":0.1,"topic":"","x":390,"y":280,"wires":[["5ae442ccaeb6ebe3"]]},{"id":"5ae442ccaeb6ebe3","type":"function","z":"f96c5c3963c0cdaf","name":"读取百分比","func":"// 协议参数\nconst ID_L = 0xfe; // 设备地址低位\nconst ID_H = 0xfe; // 设备地址高位\nconst FUNC_READ = 0x01; // 读命令\nconst ADDR_STATE = 0x02; // 电机百分比地址\nconst DATA_LEN = 0x01; // 数据长度\n\n// 构造数据帧(不含CRC)\nlet frame = Buffer.from([\n 0x55, // 起始码\n ID_L, ID_H, // 设备地址\n FUNC_READ, // 功能码\n ADDR_STATE, // 数据地址\n DATA_LEN // 数据长度\n]);\n\n// 计算CRC16-MODBUS(直接调用CRC节点函数)\n//const crc = global.get('crc').CRC16Modbus(frame);\n//const crc = global.get('crc').modbus-crc16(frame);\n//msg.payload = Buffer.concat([frame, crc]);\nmsg.payload = Buffer.concat([frame]);\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":550,"y":280,"wires":[["b12aceb362ae1279"]]},{"id":"b12aceb362ae1279","type":"modbus-crc16","z":"f96c5c3963c0cdaf","name":"","x":740,"y":280,"wires":[["43987394405da4c7","fba64588a0506317"]]},{"id":"43987394405da4c7","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":true,"complete":"true","targetType":"full","statusVal":"payload","statusType":"auto","x":950,"y":280,"wires":[]},{"id":"e06097f34ac8dc6a","type":"mqtt in","z":"f96c5c3963c0cdaf","name":"接收命令","topic":"home-assistant/cover/CiWoShaLian/set","qos":"0","datatype":"auto-detect","broker":"3f8391b19ab96685","nl":false,"rap":true,"rh":0,"inputs":0,"x":160,"y":200,"wires":[["7d5ed0c6660760c6","4b2aed372edf8c9f"]]},{"id":"7d5ed0c6660760c6","type":"switch","z":"f96c5c3963c0cdaf","name":"","property":"payload","propertyType":"msg","rules":[{"t":"eq","v":"OPEN","vt":"str"},{"t":"eq","v":"CLOSE","vt":"str"},{"t":"eq","v":"STOP","vt":"str"}],"checkall":"true","repair":false,"outputs":3,"x":310,"y":200,"wires":[["e9ab3601f48277a0"],["eed75601b8fa1756"],["086d0f1ced0a7329"]]},{"id":"575ea8f388be17f7","type":"mqtt in","z":"f96c5c3963c0cdaf","name":"接收指定位置","topic":"home-assistant/cover/CiWoShaLian/position/set","qos":"0","datatype":"buffer","broker":"3f8391b19ab96685","nl":false,"rap":true,"rh":0,"inputs":0,"x":150,"y":600,"wires":[["6d22e055d51c58da","33b34c49dfa90dce"]]},{"id":"6d22e055d51c58da","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":370,"y":760,"wires":[]},{"id":"33b34c49dfa90dce","type":"change","z":"f96c5c3963c0cdaf","name":"","rules":[{"t":"move","p":"payload","pt":"msg","to":"NUM","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":410,"y":680,"wires":[["f4284372f74ded93","ef9b37d4bda42a85"]]},{"id":"ef9b37d4bda42a85","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":630,"y":680,"wires":[]},{"id":"4b2aed372edf8c9f","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":310,"y":120,"wires":[]},{"id":"d92e138e40eed1c5","type":"tcp in","z":"f96c5c3963c0cdaf","name":"","server":"client","host":"10.10.10.46","port":"23","datamode":"stream","datatype":"buffer","newline":"","topic":"","trim":false,"base64":false,"tls":"","x":110,"y":1040,"wires":[["f62d7469a95cc304","0eb985edea8dd397"]]},{"id":"f62d7469a95cc304","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":290,"y":980,"wires":[]},{"id":"0eb985edea8dd397","type":"function","z":"f96c5c3963c0cdaf","name":"解析响应","func":"const data = msg.payload;\n\n// 基础校验\nif (data.length < 7 || data[0] !== 0x55) {\n msg.payload = '错误:无效响应帧';\n return msg;\n}\n\n// 验证设备地址\nconst id_l = data[1];\nconst id_h = data[2];\nif (id_l !== 0xfe || id_h !== 0xfe) {\n msg.payload = '错误:地址不匹配';\n return msg;\n}\n\nconst cmd = data[3];\nif (cmd == 0x01) { //确认返回数据是读取的位置指令\n// msg.payload = '错误:命令不匹配';\n// return msg;\n\n // 提取状态值\n const state = data[5]; // 百分比\n const stateMap = {\n 0x00: '关闭',\n 0x64: '打开',\n 0xff: '未设置行程'\n };\n\n msg.num = state;\n msg.str = `电机状态:${stateMap[state] || '未知'}`;\n msg.payload = `电机状态:${[state] || '未知'}`;\n return msg;\n}\n\n","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":300,"y":1040,"wires":[["982027f598f2ee49","ad78cc84995cef10"]]},{"id":"982027f598f2ee49","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":470,"y":1040,"wires":[]},{"id":"5cb857bc910511db","type":"mqtt out","z":"f96c5c3963c0cdaf","name":"位置百分比","topic":"home-assistant/cover/CiWoShaLian/position","qos":"0","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3f8391b19ab96685","x":670,"y":900,"wires":[]},{"id":"ad78cc84995cef10","type":"function","z":"f96c5c3963c0cdaf","name":"解析响应","func":"const data = msg.num;\nif(msg.num < 2)\n{ \n msg.num = 0\n}\nelse\n{\n if(msg.num > 98)\n { \n msg.num = 100\n }\n \n}\n\nmsg.payload = msg.num;\n\ndelete msg.num;\ndelete msg.str;\n\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":480,"y":960,"wires":[["7f432b4e33c70add","5367633a5c7c48f2","5cb857bc910511db","df890ab2e7f15281"]]},{"id":"7f432b4e33c70add","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":650,"y":1000,"wires":[]},{"id":"e249380eebf421c8","type":"trigger","z":"f96c5c3963c0cdaf","name":"10秒超时离线","op1":"0","op2":"0","op1type":"num","op2type":"num","duration":"10","extend":true,"overrideDelay":true,"units":"s","reset":"101","bytopic":"topic","topic":"topic","outputs":1,"x":680,"y":1240,"wires":[["c073eb08b5ee2338"]]},{"id":"c073eb08b5ee2338","type":"function","z":"f96c5c3963c0cdaf","name":"offline","func":"msg.payload = "offline";\nreturn msg;\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":1240,"wires":[["d081abfad7fdbc86"]]},{"id":"d081abfad7fdbc86","type":"mqtt out","z":"f96c5c3963c0cdaf","name":"在线状态","topic":"home-assistant/cover/CiWoShaLian/availability","qos":"0","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3f8391b19ab96685","x":1020,"y":1240,"wires":[]},{"id":"e9c2d3e2f040bb14","type":"trigger","z":"f96c5c3963c0cdaf","name":"持续发布上线","op1":"0","op2":"0","op1type":"num","op2type":"num","duration":"-10","extend":true,"overrideDelay":true,"units":"s","reset":"101","bytopic":"topic","topic":"topic","outputs":1,"x":680,"y":1300,"wires":[["b918f105286f1909"]]},{"id":"b918f105286f1909","type":"function","z":"f96c5c3963c0cdaf","name":"online","func":"msg.payload = "online";\nreturn msg;\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":1300,"wires":[["d081abfad7fdbc86"]]},{"id":"e0ba1975658c4cfe","type":"mqtt out","z":"f96c5c3963c0cdaf","name":"上报状态","topic":"home-assistant/cover/CiWoShaLian/state","qos":"0","retain":"false","respTopic":"","contentType":"","userProps":"","correl":"","expiry":"","broker":"3f8391b19ab96685","x":1020,"y":960,"wires":[]},{"id":"eddc3d24d209e796","type":"function","z":"f96c5c3963c0cdaf","name":"open","func":"msg.payload = "OPEN";\nreturn msg;\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":1040,"wires":[[]]},{"id":"2020219214e7cd23","type":"function","z":"f96c5c3963c0cdaf","name":"closed","func":"msg.payload = "CLOSED";\nreturn msg;\n\n\n","outputs":1,"timeout":0,"noerr":0,"initialize":"","finalize":"","libs":[],"x":850,"y":1100,"wires":[[]]},{"id":"5367633a5c7c48f2","type":"switch","z":"f96c5c3963c0cdaf","name":"","property":"payload","propertyType":"msg","rules":[{"t":"gt","v":"0","vt":"num"},{"t":"eq","v":"0","vt":"num"},{"t":"lt","v":"101","vt":"num"}],"checkall":"true","repair":false,"outputs":3,"x":650,"y":1080,"wires":[["eddc3d24d209e796"],["2020219214e7cd23"],["e249380eebf421c8","e9c2d3e2f040bb14"]]},{"id":"df890ab2e7f15281","type":"function","z":"f96c5c3963c0cdaf","name":"状态分析","func":"var lastpostion = msg.payload\nvar nowpostion = context.get('nowpostion') || 0;\nvar state\n\nif(lastpostion < 2)\n{ \n state='CLOSED'\n}\nelse\n{\n if(lastpostion > 98)\n { \n state='OPEN'\n }\n else\n {\n if(lastpostion > nowpostion)\n { \n state='OPENING'\n }\n else\n {\n if(lastpostion < nowpostion)\n { \n state ='CLOSING'\n }\n else\n if(lastpostion == nowpostion)\n { \n state ='STOP'\n }\n }\n }\n}\n\nnowpostion = lastpostion\ncontext.set('nowpostion', nowpostion)\nmsg.payload = state\nreturn msg;","outputs":1,"timeout":"","noerr":0,"initialize":"","finalize":"","libs":[],"x":840,"y":960,"wires":[["5d726b5533e9b93a","e0ba1975658c4cfe"]]},{"id":"5d726b5533e9b93a","type":"debug","z":"f96c5c3963c0cdaf","name":"","active":false,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":1010,"y":860,"wires":[]},{"id":"3f8391b19ab96685","type":"mqtt-broker","name":"EMQX","broker":"10.10.10.11","port":"1883","clientid":"HomeNodeRED","autoConnect":true,"usetls":false,"protocolVersion":"4","keepalive":"30","cleansession":true,"autoUnsubscribe":true,"birthTopic":"","birthQos":"0","birthRetain":"false","birthPayload":"","birthMsg":{},"closeTopic":"","closeQos":"0","closeRetain":"false","closePayload":"","closeMsg":{},"willTopic":"","willQos":"0","willRetain":"false","willPayload":"","willMsg":{},"userProps":"","sessionExpiry":""}]