node-red-contrib-symi-modbus 2.7.0 → 2.7.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -110
- package/nodes/custom-protocol.html +276 -0
- package/nodes/custom-protocol.js +204 -0
- package/nodes/modbus-dashboard.html +396 -0
- package/nodes/modbus-dashboard.js +98 -0
- package/nodes/modbus-slave-switch.js +36 -32
- package/package.json +5 -3
package/README.md
CHANGED
|
@@ -19,6 +19,8 @@ Node-RED的Modbus继电器控制节点,支持TCP/串口通信和MQTT集成,
|
|
|
19
19
|
- **Symi开关集成**:自动识别并处理Symi私有协议按键事件,实现开关面板与继电器的双向同步
|
|
20
20
|
- **HomeKit网桥**:一键桥接到Apple HomeKit,支持Siri语音控制,自动同步主站配置,名称可自定义
|
|
21
21
|
- **智能写入队列**:所有写入操作串行执行,支持HomeKit群控160个继电器同时动作,流畅无卡顿
|
|
22
|
+
- **可视化控制看板**:实时显示和控制所有继电器状态,美观易用,适合现场调试和日常监控
|
|
23
|
+
- **自定义协议转换**:支持非标准485协议设备,窗帘循环控制,配置界面可测试发送
|
|
22
24
|
- **多设备轮询**:支持最多10台Modbus从站设备,每台32路继电器
|
|
23
25
|
- **智能轮询机制**:从站上报时自动暂停轮询,优先处理数据,避免冲突
|
|
24
26
|
- **稳定可靠**:完整的内存管理、错误处理、断线重连,适合7x24小时长期运行
|
|
@@ -680,6 +682,107 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
680
682
|
- 配置信息持久化存储在~/.node-red/homekit-persist目录
|
|
681
683
|
- 重启Node-RED后自动恢复配对状态,无需重新配对
|
|
682
684
|
|
|
685
|
+
### 控制看板节点(客户友好模式)
|
|
686
|
+
|
|
687
|
+
控制看板节点提供可视化界面,实时显示和控制所有从站的继电器状态,适合现场调试和日常监控。
|
|
688
|
+
|
|
689
|
+
**配置参数**:
|
|
690
|
+
- **节点名称**:控制看板的显示名称(默认:Modbus控制看板)
|
|
691
|
+
- **主站节点**:选择要监控的Modbus主站节点(必填)
|
|
692
|
+
|
|
693
|
+
**功能特性**:
|
|
694
|
+
- **实时状态显示**:在配置界面中显示所有从站和线圈的实时状态
|
|
695
|
+
- **一键控制**:点击按钮即可控制继电器开关,无需部署流程
|
|
696
|
+
- **美观布局**:网格布局,按从站分组显示,一目了然
|
|
697
|
+
- **名称同步**:自动同步HomeKit网桥配置的继电器名称
|
|
698
|
+
- **快速响应**:状态实时更新(500ms轮询),响应迅速
|
|
699
|
+
- **零开销**:不参与实际Modbus通信,不影响主站性能
|
|
700
|
+
|
|
701
|
+
**使用步骤**:
|
|
702
|
+
1. 在Node-RED中添加控制看板节点
|
|
703
|
+
2. 选择已配置的Modbus主站节点
|
|
704
|
+
3. 双击节点打开配置界面,即可看到所有继电器状态
|
|
705
|
+
4. 点击按钮即可控制继电器开关(绿色=ON,红色=OFF)
|
|
706
|
+
5. 部署流程后,节点会显示"监控中"状态
|
|
707
|
+
|
|
708
|
+
**使用场景**:
|
|
709
|
+
- **现场调试**:快速测试继电器是否正常工作
|
|
710
|
+
- **日常监控**:实时查看所有继电器状态
|
|
711
|
+
- **批量控制**:快速控制多个继电器
|
|
712
|
+
- **客户演示**:美观的界面,适合向客户展示系统功能
|
|
713
|
+
|
|
714
|
+
**技术细节**:
|
|
715
|
+
- 使用HTTP API与主站通信,通过内部事件发送控制命令
|
|
716
|
+
- 状态缓存机制,减少网络请求
|
|
717
|
+
- 仅在配置界面打开时才轮询状态,关闭后自动停止
|
|
718
|
+
- 与HomeKit网桥共享继电器名称配置,保持一致性
|
|
719
|
+
|
|
720
|
+
**注意事项**:
|
|
721
|
+
- 确保主站节点已正确配置并运行
|
|
722
|
+
- 控制看板只在配置界面打开时才轮询状态
|
|
723
|
+
- 继电器名称需在HomeKit网桥节点中配置
|
|
724
|
+
- 本节点不参与实际Modbus通信,不会增加主站负担
|
|
725
|
+
|
|
726
|
+
### 自定义协议节点
|
|
727
|
+
|
|
728
|
+
自定义协议节点用于控制非标准Modbus协议的485设备,支持开关、窗帘、其他三种设备类型。
|
|
729
|
+
|
|
730
|
+
**配置参数**:
|
|
731
|
+
- **节点名称**:自定义协议节点的显示名称(默认:自定义协议)
|
|
732
|
+
- **设备类型**:选择设备类型(开关/窗帘/其他)
|
|
733
|
+
- **串口配置**:选择串口配置节点(必填)
|
|
734
|
+
- **打开指令**:16进制打开指令(最多48字节)
|
|
735
|
+
- **关闭指令**:16进制关闭指令(最多48字节)
|
|
736
|
+
- **暂停指令**:16进制暂停指令(仅窗帘模式,最多48字节)
|
|
737
|
+
|
|
738
|
+
**设备类型说明**:
|
|
739
|
+
- **开关模式**:接收`true`发送打开指令,接收`false`发送关闭指令
|
|
740
|
+
- **窗帘模式**:无论收到`true`还是`false`,都触发下一个指令,循环顺序:打开 → 暂停 → 关闭 → 暂停 → 打开...
|
|
741
|
+
- **其他模式**:与开关模式相同,接收`true/false`发送对应指令
|
|
742
|
+
|
|
743
|
+
**功能特性**:
|
|
744
|
+
- **16进制配置**:支持空格分隔的16进制码,自动格式化为大写
|
|
745
|
+
- **字节限制**:每个指令最多48字节,超出自动截断
|
|
746
|
+
- **测试功能**:配置界面可直接点击"测试"按钮发送指令到串口总线
|
|
747
|
+
- **持久化保存**:配置自动保存,重启后自动恢复
|
|
748
|
+
- **连线方式**:从站开关 → 自定义协议 → debug节点
|
|
749
|
+
|
|
750
|
+
**使用示例**:
|
|
751
|
+
1. 在Node-RED中添加自定义协议节点
|
|
752
|
+
2. 选择设备类型(例如:窗帘)
|
|
753
|
+
3. 选择串口配置节点
|
|
754
|
+
4. 输入16进制指令(例如:`01 05 00 00 FF 00 8C 3A`)
|
|
755
|
+
5. 点击"测试"按钮验证指令是否正确
|
|
756
|
+
6. 连线:从站开关 → 自定义协议 → debug节点
|
|
757
|
+
7. 部署流程,触发从站开关即可发送自定义指令
|
|
758
|
+
|
|
759
|
+
**窗帘模式示例**:
|
|
760
|
+
```
|
|
761
|
+
第1次收到true/false → 发送"打开"指令(例如:01 05 00 00 FF 00 8C 3A)
|
|
762
|
+
第2次收到true/false → 发送"暂停"指令(例如:01 05 00 01 FF 00 DD FA)
|
|
763
|
+
第3次收到true/false → 发送"关闭"指令(例如:01 05 00 00 00 00 CD CA)
|
|
764
|
+
第4次收到true/false → 发送"暂停"指令(例如:01 05 00 01 FF 00 DD FA)
|
|
765
|
+
第5次收到true/false → 循环回到"打开"指令
|
|
766
|
+
```
|
|
767
|
+
|
|
768
|
+
**适配两种开关模式**:
|
|
769
|
+
- **有状态开关**(开关模式):从站开关关联实际线圈,输出`true/false`交替
|
|
770
|
+
- **无状态开关**(场景模式):从站开关关联虚拟线圈(如线圈32),每次点击只发`true`或`false`
|
|
771
|
+
|
|
772
|
+
**技术细节**:
|
|
773
|
+
- 输入消息:`msg.payload = true/false`(从从站开关节点接收)
|
|
774
|
+
- 输出消息:`msg.payload = Buffer`(16进制数据,可连线到debug节点)
|
|
775
|
+
- 16进制字符串自动转换为Buffer格式
|
|
776
|
+
- 支持空格、大小写混合输入,自动格式化
|
|
777
|
+
- 窗帘模式内部维护状态索引,自动循环(4个状态:打开→暂停→关闭→暂停)
|
|
778
|
+
|
|
779
|
+
**注意事项**:
|
|
780
|
+
- 16进制指令最多48字节
|
|
781
|
+
- 窗帘模式需要配置三个指令(打开、关闭、暂停)
|
|
782
|
+
- 测试功能需要先选择串口配置且串口已打开
|
|
783
|
+
- 输出需要连线到debug节点才能发送到串口
|
|
784
|
+
- 非标准协议设备数量不多时推荐使用连线方式
|
|
785
|
+
|
|
683
786
|
## 输出消息格式
|
|
684
787
|
|
|
685
788
|
### 主站节点
|
|
@@ -740,7 +843,7 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
740
843
|
|
|
741
844
|
## 项目信息
|
|
742
845
|
|
|
743
|
-
**版本**: v2.7.
|
|
846
|
+
**版本**: v2.7.2
|
|
744
847
|
|
|
745
848
|
**核心功能**:
|
|
746
849
|
- 支持多种Modbus协议(Telnet ASCII、RTU over TCP、Modbus TCP、Modbus RTU串口)
|
|
@@ -749,6 +852,8 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
749
852
|
- 🔥 **免连线通信**(主站和从站通过内部事件通信,无需连线)
|
|
750
853
|
- 🔥 **HomeKit网桥**(一键桥接到Apple HomeKit,支持Siri语音控制)
|
|
751
854
|
- 🔥 **智能写入队列**(所有写入操作串行执行,避免锁竞争,支持HomeKit群控)
|
|
855
|
+
- 🔥 **可视化控制看板**(实时显示和控制所有继电器状态,美观易用)
|
|
856
|
+
- 🔥 **自定义协议转换**(支持非标准485协议设备,窗帘循环控制)
|
|
752
857
|
- MQTT集成(可选启用,Home Assistant自动发现)
|
|
753
858
|
- 物理开关面板双向同步(支持开关模式和场景模式)
|
|
754
859
|
- 长期稳定运行(内存管理、智能重连、异步处理)
|
|
@@ -757,98 +862,35 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
757
862
|
- Node.js: >=14.0.0
|
|
758
863
|
- Node-RED: >=2.0.0
|
|
759
864
|
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
- **性能优化**:
|
|
768
|
-
- 轮询恢复时间从100ms优化到20ms,提升响应速度5倍
|
|
769
|
-
- 锁等待超时从1000ms降低到100ms,快速检测异常
|
|
770
|
-
- 内存占用更低,CPU占用更少,适合长期运行
|
|
771
|
-
|
|
772
|
-
**v2.6.8更新**:
|
|
773
|
-
- **🔥 新增HomeKit网桥节点**:
|
|
774
|
-
- 一键桥接Modbus继电器到Apple HomeKit
|
|
775
|
-
- 自动同步主站配置的所有从站和继电器
|
|
776
|
-
- 支持在Node-RED中配置继电器友好名称
|
|
777
|
-
- 双向同步:HomeKit控制继电器,继电器状态实时同步到HomeKit
|
|
778
|
-
- 支持Siri语音控制和HomeKit自动化场景
|
|
779
|
-
- 持久化存储配对信息,重启后自动恢复
|
|
780
|
-
- 线圈0-15显示为开关,线圈16-31显示为插座(避免误触发)
|
|
781
|
-
|
|
782
|
-
**v2.6.7更新**:
|
|
783
|
-
- **🔥 修复LED反馈功能**:
|
|
784
|
-
- 主站轮询检测到状态变化时自动广播事件
|
|
785
|
-
- 从站开关节点监听状态变化并发送LED反馈到物理面板
|
|
786
|
-
- 支持开关模式(SET协议)和场景模式(REPORT协议)
|
|
787
|
-
- LED反馈队列间隔20ms,避免总线拥堵
|
|
788
|
-
- **🔥 TCP连接稳定性优化**:
|
|
789
|
-
- 禁用TCP超时(永久连接),避免无数据时超时断开
|
|
790
|
-
- Keep-Alive间隔从30秒优化到10秒
|
|
791
|
-
- 适应客户长期不在家、总线无数据的场景
|
|
792
|
-
- **日志优化**:
|
|
793
|
-
- 移除频繁的轮询日志("轮询持续运行中"、"正在轮询从站X")
|
|
794
|
-
- 轮询永久稳定运行,无需频繁确认
|
|
795
|
-
- 减少日志输出,降低硬盘占用
|
|
796
|
-
|
|
797
|
-
**v2.6.6更新**:
|
|
798
|
-
- **🔥 彻底解决MQTT日志刷屏问题**:
|
|
799
|
-
- 局域网IP检测优化:配置192.168.x.x等IP后不再尝试fallback地址
|
|
800
|
-
- 高频MQTT日志改为debug级别:broker候选、认证、重连等
|
|
801
|
-
- 默认不输出到日志文件和调试窗口
|
|
802
|
-
- 配置局域网IP后立即连接,不产生多余日志
|
|
803
|
-
- **日志输出优化**:
|
|
804
|
-
- 连接成功/失败:仍使用log(重要信息)
|
|
805
|
-
- 重连尝试、fallback地址、认证信息:改为debug(调试信息)
|
|
806
|
-
- 仅启用debug模式时才在调试窗口显示
|
|
807
|
-
- 彻底解决日志刷屏和硬盘占用问题
|
|
808
|
-
|
|
809
|
-
**v2.6.5更新**:
|
|
810
|
-
- **🔥 修复MQTT报错问题**:从站开关节点新增"启用MQTT"勾选框
|
|
811
|
-
- 默认不启用MQTT,不会尝试连接
|
|
812
|
-
- 本地模式和MQTT模式自由切换
|
|
813
|
-
|
|
814
|
-
**v2.6.4更新**:
|
|
815
|
-
- **🔥 日志优化**:大幅减少日志输出,保证长期稳定运行
|
|
816
|
-
- 高频操作日志改为debug级别
|
|
817
|
-
- 默认不输出到日志文件
|
|
818
|
-
- 完善的内存清理机制
|
|
819
|
-
|
|
820
|
-
**v2.6.3更新**:
|
|
821
|
-
- **🔥 MQTT可选配置**:完全兼容无MQTT环境
|
|
822
|
-
- 本地模式:纯串口通信
|
|
823
|
-
- MQTT模式:可选接入HA
|
|
824
|
-
- 智能切换和状态显示
|
|
825
|
-
|
|
826
|
-
**性能优化**:
|
|
827
|
-
- 轮询间隔优化:修复间隔计算逻辑,确保每个从站使用正确的轮询间隔
|
|
828
|
-
- MQTT发布使用QoS=0,避免阻塞轮询
|
|
829
|
-
- 异步发布状态更新,不影响Modbus读取性能
|
|
830
|
-
- 减少调试日志输出,降低CPU占用
|
|
831
|
-
- 互斥锁机制确保读写操作不冲突
|
|
832
|
-
- 共享连接配置节点,避免串口资源冲突
|
|
833
|
-
|
|
834
|
-
**许可证**: MIT License
|
|
835
|
-
|
|
836
|
-
**作者**: symi-daguo
|
|
865
|
+
## 许可证
|
|
866
|
+
|
|
867
|
+
MIT License
|
|
868
|
+
|
|
869
|
+
## 作者
|
|
870
|
+
|
|
871
|
+
symi-daguo
|
|
837
872
|
- NPM: https://www.npmjs.com/~symi-daguo
|
|
838
873
|
- GitHub: https://github.com/symi-daguo
|
|
839
874
|
|
|
840
|
-
|
|
875
|
+
## 支持
|
|
876
|
+
|
|
841
877
|
- Issues: https://github.com/symi-daguo/node-red-contrib-symi-modbus/issues
|
|
842
878
|
- NPM: https://www.npmjs.com/package/node-red-contrib-symi-modbus
|
|
843
879
|
|
|
844
880
|
### 节点与分类(Palette)
|
|
845
881
|
|
|
846
882
|
- 侧边栏分类名:`SYMI-MODBUS`
|
|
847
|
-
-
|
|
883
|
+
- 包含节点:
|
|
884
|
+
- `modbus-master`(主站)
|
|
885
|
+
- `modbus-slave-switch`(从站开关)
|
|
886
|
+
- `modbus-dashboard`(控制看板)
|
|
887
|
+
- `homekit-bridge`(HomeKit网桥)
|
|
888
|
+
- `custom-protocol`(自定义协议)
|
|
889
|
+
- `modbus-debug`(调试)
|
|
848
890
|
- 如果未显示该分类或节点:
|
|
849
891
|
- 刷新浏览器缓存(Shift+刷新)
|
|
850
892
|
- 重启 Node-RED(如:`node-red-restart` 或系统服务方式)
|
|
851
|
-
- 在“节点管理(Manage Palette
|
|
893
|
+
- 在“节点管理(Manage Palette)”确认安装版本为最新版
|
|
852
894
|
|
|
853
895
|
### 调试节点(modbus-debug)使用要点
|
|
854
896
|
|
|
@@ -858,16 +900,6 @@ HomeKit网桥节点无需输入消息,自动同步主站配置和状态。
|
|
|
858
900
|
- HEX显示:可选大写、可选时间戳、`maxBytes` 控制显示长度
|
|
859
901
|
- 输出:`msg.payload`(格式化HEX)、`msg.buffer`(原始Buffer)、`msg.meta`(来源信息)
|
|
860
902
|
|
|
861
|
-
### v2.6.6 重要更新
|
|
862
|
-
|
|
863
|
-
- 调试节点配置验证修复:根据所选数据来源类型(`serial`/`modbus`)动态校验,避免误报为“配置不正确”
|
|
864
|
-
- 统一侧边栏分类:所有节点统一归类到 `SYMI-MODBUS`
|
|
865
|
-
- 文档更新:补充节点分类、调试节点要点与常见问题处理
|
|
866
|
-
|
|
867
|
-
典型用法:
|
|
868
|
-
- 联调TCP转RS485网关时,观察上行/下行原始数据帧是否完整。
|
|
869
|
-
- 排查波特率/数据位/校验位配置是否正确(串口模式)。
|
|
870
|
-
- 与 `modbus-master` 或 `modbus-slave-switch` 同时使用,定位现场设备通信异常。
|
|
871
903
|
|
|
872
904
|
### MQTT自动发现
|
|
873
905
|
|
|
@@ -1106,27 +1138,26 @@ msg.payload = 1; // 或 0
|
|
|
1106
1138
|
|
|
1107
1139
|
## 项目信息
|
|
1108
1140
|
|
|
1109
|
-
|
|
1141
|
+
**当前版本**: v2.7.3
|
|
1110
1142
|
|
|
1111
|
-
|
|
1112
|
-
-
|
|
1113
|
-
-
|
|
1114
|
-
-
|
|
1115
|
-
-
|
|
1116
|
-
- 🔥 **双模式支持**(本地模式和MQTT模式可选切换,断网也能稳定运行)
|
|
1117
|
-
- 🔥 **免连线通信**(主站和从站通过内部事件通信,无需连线,支持本地模式和MQTT模式)
|
|
1118
|
-
- 🔥 **HomeKit网桥**(一键桥接到Apple HomeKit,支持Siri语音控制,名称可自定义)
|
|
1119
|
-
- 🔥 **智能写入队列**(所有写入操作串行执行,避免锁竞争,支持HomeKit群控)
|
|
1120
|
-
- MQTT集成(可选启用,Home Assistant自动发现,实体唯一性保证,QoS=0高性能发布)
|
|
1121
|
-
- 物理开关面板双向同步(亖米协议支持,LED反馈同步,支持开关模式和场景模式)
|
|
1122
|
-
- 共享连接架构(多个从站开关节点共享同一个串口/TCP连接,支持500+节点)
|
|
1123
|
-
- 长期稳定运行(内存管理、智能重连、错误日志限流、异步MQTT发布、TCP永久连接)
|
|
1143
|
+
**最新更新**(v2.7.3):
|
|
1144
|
+
- 优化窗帘控制逻辑:打开 → 暂停 → 关闭 → 暂停 → 打开(循环),更符合实际使用场景
|
|
1145
|
+
- 优化MQTT断网日志:长期断网时减少日志输出,避免垃圾日志影响性能和硬盘空间
|
|
1146
|
+
- 重试间隔优化:从5秒改为30秒,减少重试频率,降低系统负担
|
|
1147
|
+
- 日志级别优化:MQTT错误从error改为debug,不写入日志文件
|
|
1124
1148
|
|
|
1125
1149
|
**技术栈**:
|
|
1126
|
-
- modbus-serial: ^8.0.23(内部封装serialport,支持TCP和串口)
|
|
1127
|
-
- serialport: ^12.0.0(原生串口通信)
|
|
1128
|
-
- mqtt: ^5.14.1(最新稳定版,可选依赖)
|
|
1129
|
-
- hap-nodejs: ^1.2.0(HomeKit桥接)
|
|
1130
|
-
- node-persist: ^4.0.4(持久化存储)
|
|
1131
1150
|
- Node.js: >=14.0.0
|
|
1132
1151
|
- Node-RED: >=2.0.0
|
|
1152
|
+
- modbus-serial: ^8.0.23
|
|
1153
|
+
- serialport: ^12.0.0
|
|
1154
|
+
- mqtt: ^5.14.1(可选)
|
|
1155
|
+
- hap-nodejs: ^1.2.0
|
|
1156
|
+
- node-persist: ^4.0.4
|
|
1157
|
+
|
|
1158
|
+
**历史版本**:
|
|
1159
|
+
- v2.7.2: 新增自定义协议节点,支持非标准485协议设备
|
|
1160
|
+
- v2.7.1: 新增可视化控制看板节点
|
|
1161
|
+
- v2.7.0: 智能写入队列机制,支持HomeKit群控
|
|
1162
|
+
- v2.6.8: 新增HomeKit网桥节点
|
|
1163
|
+
- v2.6.7及更早: 基础功能实现
|
|
@@ -0,0 +1,276 @@
|
|
|
1
|
+
<script type="text/javascript">
|
|
2
|
+
RED.nodes.registerType('custom-protocol', {
|
|
3
|
+
category: 'SYMI-MODBUS',
|
|
4
|
+
color: '#FF9800',
|
|
5
|
+
defaults: {
|
|
6
|
+
name: {value: "自定义协议"},
|
|
7
|
+
deviceType: {value: "switch", required: true},
|
|
8
|
+
serialConfig: {value: "", type: "serial-port-config", required: true},
|
|
9
|
+
openCmd: {value: ""},
|
|
10
|
+
closeCmd: {value: ""},
|
|
11
|
+
pauseCmd: {value: ""}
|
|
12
|
+
},
|
|
13
|
+
inputs: 1,
|
|
14
|
+
outputs: 1,
|
|
15
|
+
icon: "font-awesome/fa-code",
|
|
16
|
+
label: function() {
|
|
17
|
+
var typeNames = {
|
|
18
|
+
'switch': '开关',
|
|
19
|
+
'curtain': '窗帘',
|
|
20
|
+
'other': '其他'
|
|
21
|
+
};
|
|
22
|
+
var typeName = typeNames[this.deviceType] || '自定义';
|
|
23
|
+
return this.name || (typeName + "协议");
|
|
24
|
+
},
|
|
25
|
+
oneditprepare: function() {
|
|
26
|
+
var node = this;
|
|
27
|
+
|
|
28
|
+
// 设备类型切换
|
|
29
|
+
$("#node-input-deviceType").on("change", function() {
|
|
30
|
+
var deviceType = $(this).val();
|
|
31
|
+
if (deviceType === "curtain") {
|
|
32
|
+
$("#pause-cmd-row").show();
|
|
33
|
+
} else {
|
|
34
|
+
$("#pause-cmd-row").hide();
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
// 初始化显示
|
|
39
|
+
if (node.deviceType === "curtain") {
|
|
40
|
+
$("#pause-cmd-row").show();
|
|
41
|
+
} else {
|
|
42
|
+
$("#pause-cmd-row").hide();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 16进制输入验证和格式化
|
|
46
|
+
function validateHexInput(input) {
|
|
47
|
+
var value = $(input).val().trim();
|
|
48
|
+
// 移除所有非16进制字符
|
|
49
|
+
value = value.replace(/[^0-9A-Fa-f\s]/g, '');
|
|
50
|
+
// 自动添加空格分隔
|
|
51
|
+
value = value.replace(/\s+/g, '').match(/.{1,2}/g);
|
|
52
|
+
if (value) {
|
|
53
|
+
value = value.join(' ').toUpperCase();
|
|
54
|
+
// 限制48字节
|
|
55
|
+
var bytes = value.split(' ');
|
|
56
|
+
if (bytes.length > 48) {
|
|
57
|
+
bytes = bytes.slice(0, 48);
|
|
58
|
+
value = bytes.join(' ');
|
|
59
|
+
RED.notify('指令长度已限制为48字节', 'warning');
|
|
60
|
+
}
|
|
61
|
+
} else {
|
|
62
|
+
value = '';
|
|
63
|
+
}
|
|
64
|
+
$(input).val(value);
|
|
65
|
+
return value;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// 绑定输入验证
|
|
69
|
+
$("#node-input-openCmd, #node-input-closeCmd, #node-input-pauseCmd").on("blur", function() {
|
|
70
|
+
validateHexInput(this);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
// 测试按钮功能
|
|
74
|
+
function sendTestCommand(hexString, cmdName) {
|
|
75
|
+
if (!hexString || hexString.trim() === '') {
|
|
76
|
+
RED.notify('请先输入16进制指令', 'warning');
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
var serialConfig = $("#node-input-serialConfig").val();
|
|
81
|
+
if (!serialConfig) {
|
|
82
|
+
RED.notify('请先选择串口配置', 'warning');
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 发送测试命令
|
|
87
|
+
$.ajax({
|
|
88
|
+
url: '/custom-protocol/test',
|
|
89
|
+
method: 'POST',
|
|
90
|
+
contentType: 'application/json',
|
|
91
|
+
data: JSON.stringify({
|
|
92
|
+
serialConfig: serialConfig,
|
|
93
|
+
hexString: hexString,
|
|
94
|
+
cmdName: cmdName
|
|
95
|
+
}),
|
|
96
|
+
success: function(result) {
|
|
97
|
+
if (result.success) {
|
|
98
|
+
RED.notify('✓ ' + cmdName + '指令已发送: ' + hexString, 'success');
|
|
99
|
+
} else {
|
|
100
|
+
RED.notify('✗ 发送失败: ' + result.error, 'error');
|
|
101
|
+
}
|
|
102
|
+
},
|
|
103
|
+
error: function(err) {
|
|
104
|
+
RED.notify('✗ 发送失败: ' + err.statusText, 'error');
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// 绑定测试按钮
|
|
110
|
+
$("#btn-test-open").on("click", function() {
|
|
111
|
+
var hexString = validateHexInput("#node-input-openCmd");
|
|
112
|
+
sendTestCommand(hexString, '打开');
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
$("#btn-test-close").on("click", function() {
|
|
116
|
+
var hexString = validateHexInput("#node-input-closeCmd");
|
|
117
|
+
sendTestCommand(hexString, '关闭');
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
$("#btn-test-pause").on("click", function() {
|
|
121
|
+
var hexString = validateHexInput("#node-input-pauseCmd");
|
|
122
|
+
sendTestCommand(hexString, '暂停');
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
oneditsave: function() {
|
|
126
|
+
// 保存前验证和格式化
|
|
127
|
+
validateHexInput("#node-input-openCmd");
|
|
128
|
+
validateHexInput("#node-input-closeCmd");
|
|
129
|
+
if (this.deviceType === "curtain") {
|
|
130
|
+
validateHexInput("#node-input-pauseCmd");
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// 辅助函数
|
|
136
|
+
function validateHexInput(selector) {
|
|
137
|
+
var input = $(selector);
|
|
138
|
+
var value = input.val().trim();
|
|
139
|
+
value = value.replace(/[^0-9A-Fa-f\s]/g, '');
|
|
140
|
+
value = value.replace(/\s+/g, '').match(/.{1,2}/g);
|
|
141
|
+
if (value) {
|
|
142
|
+
value = value.join(' ').toUpperCase();
|
|
143
|
+
var bytes = value.split(' ');
|
|
144
|
+
if (bytes.length > 48) {
|
|
145
|
+
bytes = bytes.slice(0, 48);
|
|
146
|
+
value = bytes.join(' ');
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
value = '';
|
|
150
|
+
}
|
|
151
|
+
input.val(value);
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<script type="text/html" data-template-name="custom-protocol">
|
|
157
|
+
<div class="form-row">
|
|
158
|
+
<label for="node-input-name"><i class="fa fa-tag"></i> 节点名称</label>
|
|
159
|
+
<input type="text" id="node-input-name" placeholder="自定义协议">
|
|
160
|
+
</div>
|
|
161
|
+
|
|
162
|
+
<div class="form-row">
|
|
163
|
+
<label for="node-input-deviceType"><i class="fa fa-cog"></i> 设备类型</label>
|
|
164
|
+
<select id="node-input-deviceType" style="width: 70%;">
|
|
165
|
+
<option value="switch">开关(打开/关闭)</option>
|
|
166
|
+
<option value="curtain">窗帘(打开/关闭/暂停循环)</option>
|
|
167
|
+
<option value="other">其他(打开/关闭)</option>
|
|
168
|
+
</select>
|
|
169
|
+
</div>
|
|
170
|
+
|
|
171
|
+
<div class="form-row">
|
|
172
|
+
<label for="node-input-serialConfig"><i class="fa fa-plug"></i> 串口配置</label>
|
|
173
|
+
<input type="text" id="node-input-serialConfig" style="width: 70%;">
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="form-row">
|
|
177
|
+
<label style="width: 100%; font-weight: bold; margin-top: 15px; margin-bottom: 10px;">
|
|
178
|
+
<i class="fa fa-code"></i> 16进制指令配置(最多48字节)
|
|
179
|
+
</label>
|
|
180
|
+
</div>
|
|
181
|
+
|
|
182
|
+
<div class="form-row">
|
|
183
|
+
<label for="node-input-openCmd"><i class="fa fa-arrow-up"></i> 打开指令</label>
|
|
184
|
+
<input type="text" id="node-input-openCmd" placeholder="例如: 01 05 00 00 FF 00 8C 3A" style="width: 50%;">
|
|
185
|
+
<button type="button" id="btn-test-open" class="red-ui-button" style="margin-left: 5px;">
|
|
186
|
+
<i class="fa fa-play"></i> 测试
|
|
187
|
+
</button>
|
|
188
|
+
<div style="font-size: 11px; color: #999; margin-top: 5px; margin-left: 105px;">
|
|
189
|
+
输入16进制码,空格分隔,自动格式化
|
|
190
|
+
</div>
|
|
191
|
+
</div>
|
|
192
|
+
|
|
193
|
+
<div class="form-row">
|
|
194
|
+
<label for="node-input-closeCmd"><i class="fa fa-arrow-down"></i> 关闭指令</label>
|
|
195
|
+
<input type="text" id="node-input-closeCmd" placeholder="例如: 01 05 00 00 00 00 CD CA" style="width: 50%;">
|
|
196
|
+
<button type="button" id="btn-test-close" class="red-ui-button" style="margin-left: 5px;">
|
|
197
|
+
<i class="fa fa-play"></i> 测试
|
|
198
|
+
</button>
|
|
199
|
+
</div>
|
|
200
|
+
|
|
201
|
+
<div class="form-row" id="pause-cmd-row" style="display: none;">
|
|
202
|
+
<label for="node-input-pauseCmd"><i class="fa fa-pause"></i> 暂停指令</label>
|
|
203
|
+
<input type="text" id="node-input-pauseCmd" placeholder="例如: 01 05 00 01 FF 00 DD FA" style="width: 50%;">
|
|
204
|
+
<button type="button" id="btn-test-pause" class="red-ui-button" style="margin-left: 5px;">
|
|
205
|
+
<i class="fa fa-play"></i> 测试
|
|
206
|
+
</button>
|
|
207
|
+
<div style="font-size: 11px; color: #999; margin-top: 5px; margin-left: 105px;">
|
|
208
|
+
仅窗帘模式需要配置
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
|
|
212
|
+
<div class="form-row" style="margin-top: 20px;">
|
|
213
|
+
<div style="padding: 10px; background: #f0f8ff; border-left: 4px solid #2196F3; border-radius: 4px;">
|
|
214
|
+
<strong>使用说明:</strong><br>
|
|
215
|
+
<ul style="margin: 5px 0; padding-left: 20px; font-size: 12px;">
|
|
216
|
+
<li><strong>开关/其他模式</strong>: true发送打开指令,false发送关闭指令</li>
|
|
217
|
+
<li><strong>窗帘模式</strong>: true/false交替触发,循环发送打开→关闭→暂停→打开...</li>
|
|
218
|
+
<li>16进制码自动格式化为大写,空格分隔</li>
|
|
219
|
+
<li>点击"测试"按钮可直接发送到串口总线验证</li>
|
|
220
|
+
<li>配置自动持久化保存</li>
|
|
221
|
+
</ul>
|
|
222
|
+
</div>
|
|
223
|
+
</div>
|
|
224
|
+
</script>
|
|
225
|
+
|
|
226
|
+
<script type="text/html" data-help-name="custom-protocol">
|
|
227
|
+
<p>自定义协议节点,用于控制非标准Modbus协议的485设备。</p>
|
|
228
|
+
|
|
229
|
+
<h3>功能特性</h3>
|
|
230
|
+
<ul>
|
|
231
|
+
<li>支持三种设备类型:开关、窗帘、其他</li>
|
|
232
|
+
<li>窗帘模式支持打开/关闭/暂停循环控制</li>
|
|
233
|
+
<li>16进制指令配置,最多48字节</li>
|
|
234
|
+
<li>配置界面可直接测试发送指令</li>
|
|
235
|
+
<li>配置持久化保存</li>
|
|
236
|
+
</ul>
|
|
237
|
+
|
|
238
|
+
<h3>设备类型说明</h3>
|
|
239
|
+
<dl>
|
|
240
|
+
<dt>开关模式</dt>
|
|
241
|
+
<dd>接收true发送打开指令,接收false发送关闭指令</dd>
|
|
242
|
+
|
|
243
|
+
<dt>窗帘模式</dt>
|
|
244
|
+
<dd>true/false交替触发,循环发送:打开→关闭→暂停→打开...</dd>
|
|
245
|
+
|
|
246
|
+
<dt>其他模式</dt>
|
|
247
|
+
<dd>与开关模式相同,接收true/false发送对应指令</dd>
|
|
248
|
+
</dl>
|
|
249
|
+
|
|
250
|
+
<h3>输入消息</h3>
|
|
251
|
+
<p>从从站开关节点接收:</p>
|
|
252
|
+
<pre>msg.payload = true/false</pre>
|
|
253
|
+
|
|
254
|
+
<h3>输出消息</h3>
|
|
255
|
+
<p>输出Buffer格式的16进制数据,可连线到debug节点发送到串口:</p>
|
|
256
|
+
<pre>msg.payload = Buffer</pre>
|
|
257
|
+
|
|
258
|
+
<h3>使用示例</h3>
|
|
259
|
+
<ol>
|
|
260
|
+
<li>配置串口节点</li>
|
|
261
|
+
<li>选择设备类型(开关/窗帘/其他)</li>
|
|
262
|
+
<li>输入16进制指令(空格分隔)</li>
|
|
263
|
+
<li>点击"测试"按钮验证指令是否正确</li>
|
|
264
|
+
<li>连线:从站开关 → 自定义协议 → debug节点</li>
|
|
265
|
+
<li>部署流程,触发从站开关即可发送自定义指令</li>
|
|
266
|
+
</ol>
|
|
267
|
+
|
|
268
|
+
<h3>注意事项</h3>
|
|
269
|
+
<ul>
|
|
270
|
+
<li>16进制指令最多48字节</li>
|
|
271
|
+
<li>窗帘模式需要配置三个指令(打开、关闭、暂停)</li>
|
|
272
|
+
<li>测试功能需要先选择串口配置</li>
|
|
273
|
+
<li>输出需要连线到debug节点才能发送到串口</li>
|
|
274
|
+
</ul>
|
|
275
|
+
</script>
|
|
276
|
+
|