feat: add SETUP/APPS status LEDs (b2/b1), toggle on activation
Made-with: Cursor
This commit is contained in:
@@ -9,6 +9,7 @@ const { collect } = require('./metrics');
|
|||||||
const { getDashboardInfo, startTtyd, FrpcManager } = require('./frpc'); // getDashboardInfo 也用于心跳中定期刷新
|
const { getDashboardInfo, startTtyd, FrpcManager } = require('./frpc'); // getDashboardInfo 也用于心跳中定期刷新
|
||||||
const { ProvisionManager } = require('./provisioning');
|
const { ProvisionManager } = require('./provisioning');
|
||||||
const { hasInternet } = require('./network');
|
const { hasInternet } = require('./network');
|
||||||
|
const led = require('./led');
|
||||||
|
|
||||||
const MAX_BACKOFF_MS = 60_000;
|
const MAX_BACKOFF_MS = 60_000;
|
||||||
const PONG_TIMEOUT_MS = 15_000;
|
const PONG_TIMEOUT_MS = 15_000;
|
||||||
@@ -61,6 +62,9 @@ class ClawClient {
|
|||||||
async start() {
|
async start() {
|
||||||
log.info('clawd', `启动中... 服务器 = ${this._cfg.server}`);
|
log.info('clawd', `启动中... 服务器 = ${this._cfg.server}`);
|
||||||
|
|
||||||
|
// 启动时默认 SETUP 亮(未激活状态),连接成功后按实际状态切换
|
||||||
|
led.status.setSetup();
|
||||||
|
|
||||||
this._startSdNotify();
|
this._startSdNotify();
|
||||||
|
|
||||||
// 启动 AP 配网管理器(等待已保存 WiFi 自动连接,超时再开 AP)
|
// 启动 AP 配网管理器(等待已保存 WiFi 自动连接,超时再开 AP)
|
||||||
@@ -105,6 +109,7 @@ class ClawClient {
|
|||||||
if (this._provisionMgr) { this._provisionMgr.stop(); this._provisionMgr = null; }
|
if (this._provisionMgr) { this._provisionMgr.stop(); this._provisionMgr = null; }
|
||||||
this._frpc.stop();
|
this._frpc.stop();
|
||||||
if (this._ws) this._ws.terminate();
|
if (this._ws) this._ws.terminate();
|
||||||
|
led.status.off(); // 进程退出,两灯全灭
|
||||||
this._sdNotify('STOPPING=1');
|
this._sdNotify('STOPPING=1');
|
||||||
log.info('clawd', '已停止');
|
log.info('clawd', '已停止');
|
||||||
log.close();
|
log.close();
|
||||||
@@ -234,6 +239,7 @@ class ClawClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (msg.status === 'inactive') {
|
if (msg.status === 'inactive') {
|
||||||
|
led.status.setSetup(); // 未激活 → SETUP 亮
|
||||||
const id = String(msg.claw_id).padEnd(6);
|
const id = String(msg.claw_id).padEnd(6);
|
||||||
const pin = String(msg.pin);
|
const pin = String(msg.pin);
|
||||||
log.info('clawd', '');
|
log.info('clawd', '');
|
||||||
@@ -245,6 +251,7 @@ class ClawClient {
|
|||||||
log.info('clawd', '');
|
log.info('clawd', '');
|
||||||
log.info('clawd', '等待激活,心跳正常运行...');
|
log.info('clawd', '等待激活,心跳正常运行...');
|
||||||
} else {
|
} else {
|
||||||
|
led.status.setApps(); // 已激活 → APPS 亮
|
||||||
log.info('clawd', `已激活 claw_id = ${msg.claw_id}`);
|
log.info('clawd', `已激活 claw_id = ${msg.claw_id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
lib/led.js
53
lib/led.js
@@ -4,18 +4,22 @@ const fs = require('fs');
|
|||||||
const log = require('./logger');
|
const log = require('./logger');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WiFi 指示灯控制
|
* 前面板指示灯控制
|
||||||
*
|
*
|
||||||
* 硬件路径: /sys/devices/platform/openvfd/attr/b5
|
* WiFi 灯 (b5): 1 = 亮, 0 = 灭(正逻辑)
|
||||||
* 1 = 亮 0 = 灭
|
|
||||||
*
|
|
||||||
* LED 状态与 WiFi 状态的对应关系:
|
|
||||||
* - WiFi 已连接且互联网畅通 → 常亮
|
* - WiFi 已连接且互联网畅通 → 常亮
|
||||||
* - WiFi 连接中(正在尝试) → 闪烁
|
* - WiFi 连接中(正在尝试) → 闪烁
|
||||||
* - WiFi 未连接 / 无互联网 → 熄灭
|
* - WiFi 未连接 / 无互联网 → 熄灭
|
||||||
|
*
|
||||||
|
* SETUP 灯 (b2): 0 = 亮, 1 = 灭(反逻辑,与 APPS 互斥)
|
||||||
|
* APPS 灯 (b1): 0 = 亮, 1 = 灭(反逻辑,与 SETUP 互斥)
|
||||||
|
* - claw 未激活 → SETUP 亮,APPS 灭
|
||||||
|
* - claw 已激活 → APPS 亮,SETUP 灭
|
||||||
*/
|
*/
|
||||||
|
|
||||||
const LED_PATH = process.env.CLAWD_LED_PATH || '/sys/devices/platform/openvfd/attr/b5';
|
const LED_PATH = process.env.CLAWD_LED_PATH || '/sys/devices/platform/openvfd/attr/b5';
|
||||||
|
const SETUP_LED_PATH = '/sys/devices/platform/openvfd/attr/b2';
|
||||||
|
const APPS_LED_PATH = '/sys/devices/platform/openvfd/attr/b1';
|
||||||
const BLINK_INTERVAL_MS = 500; // 闪烁间隔(ms)
|
const BLINK_INTERVAL_MS = 500; // 闪烁间隔(ms)
|
||||||
|
|
||||||
class WifiLed {
|
class WifiLed {
|
||||||
@@ -82,5 +86,42 @@ class WifiLed {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ── SETUP / APPS 状态灯 ───────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SETUP 灯(b2)与 APPS 灯(b1)互斥控制。
|
||||||
|
* 两灯均为反逻辑:写 0 = 亮,写 1 = 灭。
|
||||||
|
*/
|
||||||
|
class StatusLed {
|
||||||
|
/** claw 未激活 → SETUP 亮,APPS 灭 */
|
||||||
|
setSetup() {
|
||||||
|
this._write(SETUP_LED_PATH, 0); // SETUP 亮
|
||||||
|
this._write(APPS_LED_PATH, 1); // APPS 灭
|
||||||
|
log.info('led', '状态灯 → SETUP(未激活)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** claw 已激活 → APPS 亮,SETUP 灭 */
|
||||||
|
setApps() {
|
||||||
|
this._write(SETUP_LED_PATH, 1); // SETUP 灭
|
||||||
|
this._write(APPS_LED_PATH, 0); // APPS 亮
|
||||||
|
log.info('led', '状态灯 → APPS(已激活)');
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 两灯全灭(进程退出时调用) */
|
||||||
|
off() {
|
||||||
|
this._write(SETUP_LED_PATH, 1);
|
||||||
|
this._write(APPS_LED_PATH, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
_write(path, val) {
|
||||||
|
try {
|
||||||
|
fs.writeFileSync(path, String(val));
|
||||||
|
} catch (e) {
|
||||||
|
log.warn('led', `写入失败 (${path}): ${e.message}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 全局单例,整个进程共用
|
// 全局单例,整个进程共用
|
||||||
module.exports = new WifiLed();
|
module.exports = new WifiLed();
|
||||||
|
module.exports.status = new StatusLed();
|
||||||
|
|||||||
Reference in New Issue
Block a user