fix(led): swap WiFi(play)/LAN(wifi+eth), LAN carrier via CLAWD_ETH_IFACE, faster alarm

- WifiLed drives play; LanLed drives wifi+eth pair (matches panel silkscreen)
- hasLanCableCarrier(): CLAWD_ETH_IFACE-only carrier when set (reliable unplug)
- LAN poll 1s; config.activated persisted for immediate setApps on boot/reconnect
- setApps double vfdOn alarm after 50ms for slow OpenVFD
- Clear activated + setSetup on credential errors
- install.sh env comment for CLAWD_ETH_IFACE

Made-with: Cursor
This commit is contained in:
stswangzhiping
2026-03-28 21:50:56 +08:00
parent 347b19a0c9
commit 8f5a8ec896
5 changed files with 50 additions and 12 deletions

View File

@@ -83,8 +83,11 @@ class ClawClient {
async start() {
log.info('clawd', `启动中... 服务器 = ${this._cfg.server}`);
// 启动时全灭WS 连接后由 _applyStatus() 按实际状态设置
// 启动时关 alarm若本地已记为已激活立即亮 pwr不等首包 connected
led.status.off();
if (this._cfg.activated) {
led.status.setApps();
}
this._startSdNotify();
@@ -213,6 +216,9 @@ class ClawClient {
this._backoff = 1_000;
this._wsFailCount = 0; // 连接成功,重置失败计数
this._hasEverConnected = true; // 标记已成功连接过
if (this._cfg.activated) {
led.status.setApps();
}
this._sendConnect();
this._startPing();
// 显示由 _onConnected 根据 status 设置,不在此处提前 showTime
@@ -332,12 +338,16 @@ class ClawClient {
log.warn('clawd', '硬件指纹不符,清除凭证重新注册...');
this._cfg.claw_id = null;
this._cfg.token = null;
this._cfg.activated = false;
config.save(this._cfg);
led.status.setSetup();
} else if (msg.msg && msg.msg.includes('invalid')) {
log.warn('clawd', '凭证无效,清除凭证重新注册...');
this._cfg.claw_id = null;
this._cfg.token = null;
this._cfg.activated = false;
config.save(this._cfg);
led.status.setSetup();
}
break;
default:
@@ -369,6 +379,8 @@ class ClawClient {
_applyStatus(msg) {
if (msg.status === 'inactive') {
this._cfg.activated = false;
config.save(this._cfg);
led.status.setSetup();
led.display.showPin(msg.pin);
const id = String(this._cfg.claw_id || '').padEnd(6);
@@ -383,6 +395,8 @@ class ClawClient {
log.info('clawd', '等待激活,心跳正常运行...');
this._updateOpenClawOrigin('0000');
} else {
this._cfg.activated = true;
config.save(this._cfg);
led.status.setApps();
led.display.showTime();
log.info('clawd', `已激活 claw_id = ${this._cfg.claw_id}`);

View File

@@ -17,6 +17,8 @@ const DEFAULTS = {
claw_id: null,
token: null,
heartbeat_interval: 30, // 秒
/** 云端已激活:用于启动/重连时立即点亮 alarmpwr不等首包 connected */
activated: false,
};
function load() {

View File

@@ -2,15 +2,15 @@
const fs = require('fs');
const log = require('./logger');
const { hasWiredCarrier } = require('./network');
const { hasLanCableCarrier } = require('./network');
/**
* OpenVFD 图标:/sys/class/leds/openvfd/led_on|led_off写入图标名
*
* 映射:
* wifi + eth 同亮/同灭 → 产品 WiFi 灯
* 映射与面板丝印一致play=WiFi 状态wifi+eth=有线链路)
* play → 产品 WiFi 灯(配网逻辑 on/off/blink
* wifi + eth 同亮/同灭 → LAN有线插拔见 hasLanCableCarrier / CLAWD_ETH_IFACE
* alarm → pwrSETUP=灭 / APPS=亮)
* play → lan有线 carrier
* BT → 无 sysfs仅日志
*
* 数码管AP/Conn/时间等):仍仅 debug 输出,不接 sysfs。
@@ -19,7 +19,7 @@ const { hasWiredCarrier } = require('./network');
*/
const BLINK_INTERVAL_MS = 500;
const LAN_POLL_MS = 3000;
const LAN_POLL_MS = 1000;
const VFD_BASE = process.env.CLAWD_OPENVFD_PATH || '/sys/class/leds/openvfd';
@@ -100,8 +100,9 @@ class WifiLed {
_write(val) {
const on = !!val;
log.debug('led', `[vfd] WiFiwifi+eth<= ${on ? 1 : 0}`);
vfdWifiPair(on);
log.debug('led', `[vfd] WiFiplay<= ${on ? 1 : 0}`);
if (on) vfdOn('play');
else vfdOff('play');
}
}
@@ -231,6 +232,8 @@ class StatusLed {
setApps() {
vfdOn('alarm');
// 部分 OpenVFD 驱动单次写入生效慢,短延迟再写一次
setTimeout(() => vfdOn('alarm'), 50);
log.debug('led', '[vfd] alarmpwr<= 1');
log.info('led', '状态灯 → APPS已激活');
}
@@ -257,20 +260,20 @@ class LanLed {
clearInterval(this._timer);
this._timer = null;
}
vfdOff('play');
vfdWifiPair(false);
this._current = null;
}
_sync() {
const up = hasWiredCarrier();
const up = hasLanCableCarrier();
if (up) {
if (this._current !== 'on') {
vfdOn('play');
vfdWifiPair(true);
this._current = 'on';
log.info('led', 'LAN有线 carrier→ 亮');
}
} else if (this._current !== 'off') {
vfdOff('play');
vfdWifiPair(false);
this._current = 'off';
log.info('led', 'LAN有线 carrier→ 灭');
}

View File

@@ -62,6 +62,22 @@ function hasWiredCarrier() {
return getWiredIfaceWithCarrier() !== null;
}
/**
* LAN 面板灯专用:若设置 CLAWD_ETH_IFACE只认该口 carrier拔网线必灭
* 未设置时退回 hasWiredCarrier()(可能受其它网口影响,建议在 box 上配置 end0 等)。
*/
function hasLanCableCarrier() {
const explicit = process.env.CLAWD_ETH_IFACE;
if (explicit) {
try {
return fs.readFileSync(`/sys/class/net/${explicit}/carrier`, 'utf8').trim() === '1';
} catch (_) {
return false;
}
}
return hasWiredCarrier();
}
function _tryPingInternet() {
try {
run('ping -c 1 -W 3 8.8.8.8');
@@ -312,6 +328,7 @@ function getLocalIps() {
module.exports = {
hasInternet,
hasWiredCarrier,
hasLanCableCarrier,
hasWiredInternetProbe,
getWiredIfaceWithCarrier,
hasSavedWifiConnection,