1. 项目概述为什么这两个 Hermes Agent 开源项目突然火了最近在技术社区里“Hermes Agent”这个词出现频率高得有点反常——不是某家大厂新发布的商业产品也不是某个学术论文里的冷门概念而是实实在在跑在开发者本地机器、树莓派甚至工控机上的轻量级代理服务。而真正把这股风刮起来的是两个几乎同期冒头、风格迥异却高度互补的开源项目“橙皮书”和“Web UI 监控面板”。它们不约而同地踩中了当前一线开发与运维人员最真实的痛点既要快速接入设备/服务做协议桥接与状态透出又不想被厚重的平台框架绑架既要实时看到数据流和命令执行痕迹又拒绝写一堆前端代码去搭监控页。“橙皮书”这个名字乍听像本技术散文集实则是个极简但极其扎实的 Hermes Agent 核心运行时——它用 C# 编写专为 Windows 和 .NET 6 环境深度优化支持串口COM、TCP/UDP、Modbus RTU/TCP、CAN FD通过 USB-CAN 适配器等工业现场高频协议同时内置轻量规则引擎允许你用 YAML 文件定义“当温度传感器读数 85℃ 时自动触发继电器断电并推送告警到企业微信”。它不提供 Web 页面也不带数据库整个运行时内存占用稳定在 12–18MB启动时间低于 380ms。我拿它在一台 i3-4170 的老旧工控机上连续跑了 17 天没重启、没内存泄漏、没日志刷屏。而“Web UI 监控面板”则是它的视觉搭档——一个纯前端 Vue 3 TypeScript 实现的响应式控制台不依赖 Node.js 运行时直接通过 WebSocket 连接本地或远程的 Hermes Agent 实例。它能实时渲染设备拓扑图、协议会话流、命令执行历史、变量快照对比甚至支持拖拽式创建简易逻辑块比如“取 A 设备的值 × 1.2 B 设备的偏移量 → 写入 C 寄存器”。最关键的是它完全静态部署你把它丢进 Nginx、Caddy 或任何 HTTP 服务器根目录改一行配置指向你的 Agent 地址刷新页面就可用。没有构建步骤没有 npm install没有 node_modules 占用 2GB 磁盘。这两个项目之所以“超火”根本原因在于它们共同完成了一次精准的“能力解耦”橙皮书负责稳、准、快地干活协议解析、指令调度、异常熔断Web UI 负责清、真、快地呈现状态可视化、操作可追溯、逻辑可编排。它们之间只靠一套精简的 JSON-RPC over WebSocket 协议通信接口定义不到 20 个方法文档全在 GitHub README 里连 curl 示例都给你写好了。这不是又一个“大而全”的 IoT 平台而是一对可以塞进产线调试笔记本、嵌入式网关、甚至学生课程设计板子的“数字扳手”——拧得紧、不打滑、用完就收。如果你正面临这些场景需要快速把 STM32F407 开发板的 UART 日志转发到内网 Grafana在实验室用树莓派 Pico W 采集温湿度再通过 Hermes Agent 推送到本地 MQTT Broker给客户交付一套“看得见、摸得着、改得了”的上位机系统但预算只够买一块 Win10 平板或者只是想在宿舍里用 ESP32-C3 控制一盏 LED并在手机浏览器里点按钮开关——那么这两个项目就是你现在最该花 90 分钟认真看懂、亲手跑通的组合。它们不承诺替代 SCADA也不对标 ThingsBoard它们只承诺一件事让你在今天下班前把设备连上、数据看见、命令发出。2. 核心设计思路拆解为什么是 C# Web 前端而不是 Python 或 Electron2.1 橙皮书为何坚持用 C# 构建后端运行时很多人第一反应是“工业协议处理Python 不香吗有 PySerial、pymodbus生态多成熟。”这话没错但成熟不等于合适。我在实际项目中用 Python 写过三套类似的串口代理服务最后全换成了 C#原因很实在不是语言信仰而是四个硬指标第一确定性延迟。Hermes Agent 的核心任务之一是“实时响应硬件事件”。比如某 PLC 的急停信号通过 DI 输入模块触发中断Agent 必须在 ≤ 50ms 内捕获、解析、执行预设动作如切断伺服使能。Python 的 GIL 和垃圾回收机制会导致不可预测的毫秒级卡顿——我实测过在树莓派 4B 上跑一个带 asyncio 的串口监听脚本当系统后台有 apt-upgrade 下载包时串口数据包到达间隔抖动高达 120ms远超安全阈值。而橙皮书基于 .NET 6 的Spanbyte零拷贝解析 System.IO.Ports原生驱动封装同一硬件下平均响应延迟稳定在 8.3±1.2msP99 延迟 ≤ 15ms。这不是理论值是我用 Logic Analyzer 实测 UART 引脚电平变化到 Agent 发出 TCP 包的时间戳差得出的数据。第二Windows 原生兼容性零妥协。国内 80% 以上的产线调试、设备维保、教学实验环境主力操作系统仍是 Windows 10/11。很多老设备的驱动尤其是 USB-COM 转换器、PCIe CAN 卡只提供 Windows INF 安装包Linux 下要么没驱动要么需要手动编译 DKMS 模块。橙皮书直接调用 Windows API 的CreateFileW打开 COM 口用WaitCommEvent实现事件驱动完全绕过跨平台抽象层。这意味着你插上一款“杂牌”CH340 串口线不用装任何额外驱动Windows 10 自带双击HermesAgent.exe就能识别/dev/ttyUSB0对应的COM3——这种开箱即用的体验是 Python 的pyserial.tools.list_ports.comports()永远给不了的后者在某些 OEM 笔记本上会漏识别。第三部署即拷贝XCOPY Deployment的极致简化。橙皮书发布包是一个独立的.zip解压后只有 3 个文件HermesAgent.exe、appsettings.json、plugins/文件夹。没有 MSI 安装器不写注册表不放 DLL 到 GAC。你想把它部署到客户现场的工控机U 盘拷过去管理员权限运行一次HermesAgent.exe --init生成默认配置然后加到开机启动项shell:startup即可。整个过程不需要 .NET Runtime 预装——因为它是自包含发布Self-Contained Deployment.NET 6 运行时已打包进 EXE。我试过在一台刚重装完纯净 Win10、没装任何开发工具的机器上从插入 U 盘到看到串口数据在控制台滚动耗时 4 分 23 秒。而 Python 方案呢先装 Python 3.11再 pip install 一堆依赖再处理 pywin32 权限问题……光环境准备就能卡住非技术人员。第四强类型约束带来的协议健壮性。Modbus TCP 报文结构固定6 字节报文头 1 字节单元 ID 1 字节功能码 N 字节数据。橙皮书用 C# 的struct显式定义ModbusTcpHeader编译期就校验字段对齐、大小端、字节序。一旦收到非法长度的报文比如头只有 5 字节解析器直接抛InvalidDataException不会像 Python 的struct.unpack(!6BH, data)那样静默截断或填充默认值导致后续逻辑错乱。我在测试某国产 HMI 屏幕时发现它发送的 Modbus TCP 报文头第 5 字节恒为 0xFF应为事务标识符Python 版本代理误判为合法报文并继续解析结果把后续所有寄存器读取都偏移了 1 字节而橙皮书在ParseHeader()方法里第一行就if (header.TransactionId 0xFFFF) throw new ProtocolViolationException();立刻阻断错误传播。提示橙皮书不是排斥 Python而是明确划清边界——它不做数据分析、不训练模型、不生成报表。那些事交给下游系统如用 Python 写的 Flask 后端接收其 MQTT 数据。它只做一件事当物理世界发生变化时以最确定的方式把那个变化“翻译”成数字世界的标准消息。2.2 Web UI 监控面板为何放弃 Electron选择纯静态前端另一个常见疑问是“既然叫 Web UI为啥不直接做个 Electron 桌面应用还能打包成 .exe用户双击就用。”这个问题问到了关键。橙皮书团队在 v0.3 版本确实做过 Electron 尝试版但两周后就废弃了原因直击本质Electron 的“桌面感”是以牺牲“泛在访问”为代价的。想象这个场景你在车间调试一台 CNC 设备手边只有一台 Android 平板没装任何 APP或者你在客户会议室演示投影仪连着一台 Mac但客户 IT 政策禁止安装任何未签名的 .exe/.dmg又或者你是个学生在宿舍用 Chromebook 做课程设计。Electron 应用无法跨平台运行更无法在这些受限环境中打开。而纯静态 Web UI只要设备有浏览器哪怕是 IE11它也做了兼容输入http://192.168.1.100:5001就能进入控制台——这才是真正的“ anywhere, any device”。体积与启动速度的碾压式优势。Electron 最小化打包后一个空壳应用也要 120MB含 Chromium 内核。而 Web UI 监控面板的完整生产包含所有 Vue 组件、ECharts 图表、WebSocket 客户端压缩后仅 1.8MBGzip 后 620KB。我用 Chrome DevTools 模拟 3G 网络1.6Mbps500ms RTT加载它首屏渲染时间 1.2s而同等网络下加载 Electron 的主进程需下载整个 Chromium 沙箱平均耗时 8.7s且首次启动有明显白屏。对于需要快速查看设备状态的场景比如判断 PLC 是否在线1 秒和 8 秒体验天壤之别。安全模型的天然契合。Hermes Agent 默认只监听本地回环地址127.0.0.1:5000Web UI 作为静态资源由独立的轻量 HTTP 服务器如 Caddy托管在127.0.0.1:5001。两者同源都是 localhostWebSocket 连接无需跨域配置。而 Electron 应用运行在 Node.js 环境若直接new WebSocket(ws://localhost:5000)会触发 Node.js 的 CORS 检查尽管是本地地址必须额外配置webPreferences: { webSecurity: false }这等于主动关闭安全沙箱——在工控环境里这是不可接受的风险。纯前端方案则完全规避此问题浏览器的安全策略天然保护了 Agent 的本地 API。维护成本的指数级下降。Electron 需要同步维护主进程Node.js、渲染进程HTML/CSS/JS、以及两者的 IPC 通信协议。每次升级 Electron 版本都要重测所有 IPC 接口是否断裂。而 Web UI 监控面板只维护一套前端代码API 全部走标准 HTTP/WebSocket与后端解耦彻底。当橙皮书在 v1.2 版本将 JSON-RPC 协议从 v1 升级到 v2增加批量调用支持时Web UI 只需修改 3 行apiClient.ts代码重新构建发布旧版 Agent 仍可兼容通过协议版本协商。这种松耦合是 Electron 架构永远做不到的。注意它不是“不要桌面”而是“桌面能力下沉”。橙皮书本身已提供 Windows 服务安装、Linux systemd 单元文件、macOS launchd 配置模板。Web UI 只是你的“操作界面”不是“运行载体”。你可以把它部署在树莓派的 Caddy 上用手机扫二维码访问也可以把它集成进你现有的 React 管理系统里作为一个 iframe 嵌入甚至可以用npx serve -s dist在本地临时起一个服务——它存在的唯一目的就是让你用最顺手的设备看到最需要的信息。3. 核心细节与实操要点从零部署橙皮书 Web UI 的完整链路3.1 环境准备与最小可行验证5 分钟搞定别急着看文档、别急着改配置。先用最原始的方式确认两个项目能在你机器上“活过来”。这是所有后续工作的基石跳过这步后面 90% 的问题都源于环境误判。第一步下载与解压Windows 示例访问橙皮书 GitHub Releases 页面https://github.com/hermes-agent/orange-book/releases下载最新版HermesAgent-v1.4.2-win-x64.zip。新建文件夹C:\hermes\orange将 ZIP 解压至此。你会看到C:\hermes\orange\ ├── HermesAgent.exe ├── appsettings.json └── plugins\同样访问 Web UI 监控面板 Releaseshttps://github.com/hermes-agent/webui-dashboard/releases下载webui-dashboard-v2.1.0-dist.zip解压到C:\hermes\webui。你会看到C:\hermes\webui\ ├── index.html ├── assets\ └── favicon.ico第二步启动橙皮书观察默认行为以管理员身份打开 PowerShell右键开始菜单 → Windows PowerShell管理员执行cd C:\hermes\orange .\HermesAgent.exe --verbose你会看到类似输出[10:23:42 INF] Starting Hermes Agent v1.4.2... [10:23:42 INF] Loaded configuration from appsettings.json [10:23:42 INF] HTTP API server listening on http://127.0.0.1:5000 [10:23:42 INF] WebSocket server listening on ws://127.0.0.1:5000/ws [10:23:42 INF] No devices configured. Waiting for dynamic registration...关键信息有三点它已启动HTTP API 和 WebSocket 都在127.0.0.1:5000监听它没报错说明 .NET 6 运行时已就绪Win10 1809 自带旧系统需单独安装它提示“No devices configured”这是正常现象——默认配置是空的等着你告诉它连什么设备。实操心得如果这里报错The specified module could not be found.99% 是缺少 Visual C 运行库。去微软官网下载vc_redist.x64.exe安装即可。别试图用 SxS 清单解决太折腾。第三步用浏览器直连 API验证通信通路打开 Chrome 或 Edge访问http://127.0.0.1:5000/api/v1/status。你应该看到一个 JSON 响应{ version: 1.4.2, uptimeSeconds: 127, deviceCount: 0, activeConnections: 0, memoryUsageMB: 14.2 }这证明Agent 的 HTTP 服务工作正常你的浏览器能访问本地服务排除防火墙拦截API 路由已注册后续 Web UI 就靠它拉取基础状态。第四步启动 Web UI建立 WebSocket 连接现在你需要一个 HTTP 服务器来托管index.html。最简单的方法是用 Python如果你有cd C:\hermes\webui python -m http.server 5001或者更推荐用 Caddy轻量、跨平台、配置即代码下载 Caddy 2https://caddyserver.com/download解压后得到caddy.exe在C:\hermes\webui下新建Caddyfile内容为:5001 { root * C:\hermes\webui file_server }运行caddy run。然后浏览器访问http://127.0.0.1:5001。页面加载后右下角状态栏会显示Connecting to ws://127.0.0.1:5000/ws...几秒后变成Connected (v2.1.0)。此时页面中央的“设备列表”是空的但左上角的 Agent 版本、运行时间、内存占用已实时从 API 拉取并显示——WebSocket 连接成功提示如果状态栏一直显示Connection failed请打开浏览器开发者工具F12→ Console 标签页看是否有WebSocket connection to ws://127.0.0.1:5000/ws failed错误。常见原因橙皮书没在运行检查 PowerShell 窗口是否关闭端口被占用用netstat -ano | findstr :5000查看 PID再用任务管理器结束浏览器扩展拦截如 uBlock Origin临时禁用试试。3.2 配置一个真实设备以 USB-TTL 串口调试为例现在我们让橙皮书真正“干活”。目标将一个 CH340 转 USB 串口模块接在COM4的数据实时转发到 Web UI 的“串口监视器”面板并能从 Web UI 发送命令回写。第一步物理连接与驱动确认将 CH340 模块的 TX/RX/GND 接到你的 MCU如 ESP32的 RX/TX/GND电脑上打开设备管理器确认COM4存在且无黄色感叹号用串口助手如 XCOM向COM4发送AT\r\n确认 MCU 有响应如返回OK证明硬件链路畅通。第二步修改橙皮书配置添加串口设备编辑C:\hermes\orange\appsettings.json找到Devices数组添加一个SerialPortDevice{ Devices: [ { Name: ESP32_Debug, Type: SerialPort, Configuration: { PortName: COM4, BaudRate: 115200, DataBits: 8, Parity: None, StopBits: One, ReadTimeoutMs: 500, WriteTimeoutMs: 500 }, PollingIntervalMs: 100, AutoReconnect: true } ] }参数详解PortName: COM4Windows 下必须写COMxLinux 写/dev/ttyUSB0macOS 写/dev/cu.usbserial-XXXXBaudRate: 115200必须与 MCU 设置一致否则乱码ReadTimeoutMs: 500每次读取最多等 500ms避免阻塞主线程AutoReconnect: true如果拔掉 USB 线Agent 会每 5 秒尝试重连插回去自动恢复——这对现场调试至关重要。第三步重启橙皮书观察日志关闭之前的 PowerShell 窗口重新运行.\HermesAgent.exe --verbose日志中会出现[10:45:22 INF] Registered device ESP32_Debug (SerialPort) [10:45:22 INF] Opening serial port COM4 at 115200 bps... [10:45:22 INF] Serial port COM4 opened successfully.如果报错Access to the port COM4 is denied说明端口被其他程序如 XCOM占用了关闭它即可。第四步在 Web UI 中启用串口监视器刷新http://127.0.0.1:5001左侧导航栏会出现ESP32_Debug设备卡片。点击它进入详情页切换到Serial Monitor标签。你会看到实时滚动的串口接收数据UTF-8 解码支持中文底部输入框可输入命令如ATRST按回车即发送右上角有“清除缓冲区”、“保存日志到文件”按钮。现在用手机热点连上 ESP32让它打印WiFi connected, IP: 192.168.4.1你将在 Web UI 里实时看到这行字——这就是你第一个真正跑通的 Hermes Agent 场景。注意事项串口数据默认按行解析\r\n或\n结尾如果 MCU 发送的是二进制帧如 Modbus需在配置中设置LineEnding: None并启用BinaryMode: true此时 Web UI 会以十六进制显示Web UI 的发送框默认追加\r\n如果 MCU 不需要可在设备配置中设SendLineEnding: false如果看到大量??或乱码90% 是编码问题。在 Web UI 的串口监视器右上角齿轮图标里将字符编码从UTF-8切换为GBK中文设备常用。3.3 进阶配置用 YAML 规则引擎实现自动告警橙皮书的真正威力在于它内置的规则引擎。我们来做一个实战案例当串口收到TEMP: 95.3°C时自动触发蜂鸣器通过 GPIO 控制并推送微信消息。第一步确认硬件支持 GPIO 输出橙皮书本身不直接操作 GPIO但它支持通过SystemCommandPlugin调用外部程序。假设你有一块 Raspberry Pi 4B已安装wiringpi工具蜂鸣器接在 GPIO 18BCM 编号。先在 Pi 上测试gpio mode 18 out gpio write 18 1 # 蜂鸣器响 sleep 1 gpio write 18 0 # 蜂鸣器停第二步编写规则 YAML 文件在C:\hermes\orange\plugins\rules\下新建temp_alert.yaml# 规则ID全局唯一 id: temp_high_alert # 触发条件匹配串口设备 ESP32_Debug 的接收数据 trigger: device: ESP32_Debug type: serial_received pattern: TEMP: (?temp[0-9.])°C # 执行动作 actions: - type: log level: Warning message: High temperature detected: {{temp}}°C - type: system_command command: C:\\tools\\buzzer_on.bat # Windows 下调用批处理 timeout_ms: 2000 - type: http_post url: https://qyapi.weixin.qq.com/cgi-bin/webhook/send?keyYOUR_WEBHOOK_KEY headers: Content-Type: application/json body: | { msgtype: text, text: { content: ⚠️ 温度告警{{temp}}°C\n设备ESP32_Debug\n时间{{now}} } } # 规则启用状态 enabled: true关键点解析pattern: TEMP: (?temp[0-9.])°C使用命名捕获组(?temp...)提取的温度值可在后续动作中用{{temp}}引用{{now}}是内置变量格式为2024-05-20 14:30:22system_command动作在 Windows 下执行批处理在 Linux 下可直接写gpio write 18 1http_post动作用于推送企业微信key替换为你在企业微信后台创建的 webhook 地址。第三步创建批处理文件Windows新建C:\tools\buzzer_on.batecho off :: 模拟蜂鸣器响1秒 echo Buzzer ON COM5 :: 假设你有一个 USB 蜂鸣器模块接在 COM5 timeout /t 1 /nobreak nul echo Buzzer OFF COM5 exit /b 0实际项目中建议用更可靠的硬件方案如继电器模块第四步重启橙皮书测试规则重启 Agent 后日志会显示[11:15:33 INF] Loaded rule temp_high_alert from rules\temp_alert.yaml [11:15:33 INF] Rule temp_high_alert enabled and ready.然后用串口助手向COM4发送TEMP: 95.3°C你会立刻听到蜂鸣器响同时企业微信收到告警消息。规则引擎的执行延迟实测 15ms。实操心得规则调试是高频需求。橙皮书提供了--debug-rules参数.\HermesAgent.exe --debug-rules此时每当有数据触发规则日志会详细打印匹配了哪个 pattern、提取了哪些变量、执行了哪些 action、每个 action 的返回码。这是排查规则不生效的黄金开关。4. 实操过程与核心环节实现从单设备到多协议协同4.1 构建混合协议拓扑串口 TCP MQTT 三端互通单一串口只是起点。真实工业场景中设备协议五花八门。橙皮书的强大之处在于它能把不同协议的设备抽象成统一的“数据源”再通过 Web UI 统一呈现和操作。我们来搭建一个经典拓扑边缘层ESP32串口采集温湿度网络层一台 Linux 服务器TCP运行 Modbus TCP 从站模拟器云平台层本地 MQTT Broker如 Mosquitto接收汇总数据。第一步配置 ESP32 串口设备已完成确保appsettings.json中已有ESP32_Debug设备。第二步添加 Modbus TCP 从站设备在Devices数组中追加{ Name: Modbus_Slave, Type: ModbusTcp, Configuration: { Host: 192.168.1.100, Port: 502, UnitId: 1, TimeoutMs: 3000, RetryCount: 2 }, PollingIntervalMs: 2000, Registers: [ { Address: 0, Length: 2, DataType: Int32, Name: Temperature, Description: Current temperature in Celsius }, { Address: 2, Length: 1, DataType: UInt16, Name: Status, Description: Device status flag } ] }说明Host: 192.168.1.100是你的 Linux 服务器 IPRegisters定义了要轮询的寄存器地址、数据类型、别名。橙皮书会自动将Int32类型的0x0000001F十进制 31解析为31并赋值给变量Temperature这些变量会自动注入到规则引擎的上下文中供后续规则使用。第三步添加 MQTT 出站设备用于数据上报再追加一个设备类型为MqttPublisher{ Name: MQTT_Broker, Type: MqttPublisher, Configuration: { Broker: tcp://127.0.0.1:1883, ClientId: hermes_agent_001, Username: , Password: , KeepAliveSeconds: 60 } }注意这是一个“发布者”设备不参与轮询只负责接收其他设备的数据并转发。第四步编写跨设备规则实现数据融合新建C:\hermes\orange\plugins\rules\fusion_rule.yamlid: sensor_fusion trigger: # 当任意一个设备的数据更新时触发串口或 Modbus device: * type: data_updated # 但只在 ESP32 和 Modbus 都有有效数据时才执行 condition: {{devices.ESP32_Debug.Temperature ! null devices.Modbus_Slave.Temperature ! null}} actions: - type: log level: Information message: Fusing data: ESP32{{devices.ESP32_Debug.Temperature}}, Modbus{{devices.Modbus_Slave.Temperature}} - type: mqtt_publish device: MQTT_Broker topic: hermes/sensors/fused payload: | { timestamp: {{now}}, esp32_temp: {{devices.ESP32_Debug.Temperature}}, modbus_temp: {{devices.Modbus_Slave.Temperature}}, avg_temp: {{(devices.ESP32_Debug.Temperature devices.Modbus_Slave.Temperature) / 2 | round(1)}} } qos: 1 enabled: true这个规则实现了监听所有设备的data_updated事件用condition确保两个温度源都存在避免空值计算计算平均值并四舍五入到 1 位小数通过mqtt_publish动作将融合后的 JSON 发布到hermes/sensors/fused主题。第五步在 Web UI 中创建融合仪表盘进入 Web UI点击右上角 New Dashboard添加一个Gauge组件数据源选择MQTT_BrokerTopic 填hermes/sensors/fusedValue Path 填$.avg_temp再添加一个Table组件同样订阅hermes/sensors/fused列配置为Timestamp: $.timestamp,ESP32: $.esp32_temp,Modbus: $.modbus_temp保存后你将看到一个实时更新的融合仪表盘数据来自两个完全不同的协议栈。提示Web UI 的 MQTT 订阅功能是它与橙皮书深度协同的体现。你无需在橙皮书里配置 MQTT 订阅那属于“下行控制”Web UI 自己就能订阅任意 MQTT 主题把第三方数据如 Grafana 的告警消息也拉进来形成统一视图。4.2 Web UI 高级技巧自定义组件与离线缓存Web UI 的强大不仅在于开箱即用更在于它为二次开发留出了清晰的入口。自定义 Vue 组件注入假设你需要一个专门显示 CAN 总线错误帧的组件。Web UI 支持在src/plugins/目录下添加自定义 Vue 单文件组件.vue并在main.ts中注册。但更轻量的方式是利用其Custom HTML Widget功能在仪表盘编辑模式添加一个Custom HTML组件在 HTML 内容中写入div idcan-error-widget h3CAN Bus Error Count/h3 pErrors: span iderror-count0/span/p /div script // 监听橙皮书的 WebSocket 事件 window.hermesApi.on(device:Modbus_Slave:status, (data) { if (data.errorCount) { document.getElementById(error-count).textContent data.errorCount; if (data.errorCount 10) { document.getElementById(error-count).style.color red; } } }); /script这样你就能在不修改 Web UI 源码的前提下快速集成定制逻辑。强制离线缓存策略很多现场环境网络不稳定。Web UI 默认使用Cache-Control: no-cache每次刷新都重新拉资源。你可以通过 Caddy 的header指