diff --git a/lib/led.js b/lib/led.js index 2119311..31655db 100644 --- a/lib/led.js +++ b/lib/led.js @@ -12,15 +12,21 @@ const log = require('./logger'); * - WiFi 连接中(正在尝试) → 闪烁 * - WiFi 未连接 / 无互联网 → 熄灭 * + * BT 灯 (b6): 1 = 亮, 0 = 灭(正逻辑) + * - BLE 配网进行中 → 闪烁 + * - BLE 配网成功 → 常亮 + * - 蓝牙不工作 → 熄灭 + * * 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 SETUP_LED_PATH = '/sys/devices/platform/openvfd/attr/b1'; // 物理 SETUP 灯 -const APPS_LED_PATH = '/sys/devices/platform/openvfd/attr/b2'; // 物理 APPS 灯 +const LED_PATH = process.env.CLAWD_LED_PATH || '/sys/devices/platform/openvfd/attr/b5'; +const BT_LED_PATH = process.env.CLAWD_BT_LED_PATH || '/sys/devices/platform/openvfd/attr/b6'; +const SETUP_LED_PATH = '/sys/devices/platform/openvfd/attr/b1'; // 物理 SETUP 灯 +const APPS_LED_PATH = '/sys/devices/platform/openvfd/attr/b2'; // 物理 APPS 灯 const BLINK_INTERVAL_MS = 500; // 闪烁间隔(ms) class WifiLed { @@ -87,6 +93,76 @@ class WifiLed { } } +// ── 蓝牙指示灯 ─────────────────────────────────────────────────────────────── + +/** + * BT 指示灯(b6)正逻辑:1 = 亮,0 = 灭。 + * blink() — BLE 配网进行中 + * on() — BLE 配网成功 / 蓝牙功能正常 + * off() — 蓝牙不工作 + */ +class BtLed { + constructor() { + this._blinkTimer = null; + this._blinkState = false; + this._current = null; // 'on' | 'off' | 'blink' + } + + /** 常亮(配网成功) */ + on() { + if (this._current === 'on') return; + this._stopBlink(); + this._write(1); + this._current = 'on'; + log.info('led', 'BT 指示灯 → 常亮'); + } + + /** 熄灭(蓝牙不工作) */ + off() { + if (this._current === 'off') return; + this._stopBlink(); + this._write(0); + this._current = 'off'; + log.info('led', 'BT 指示灯 → 熄灭'); + } + + /** 闪烁(BLE 配网进行中) */ + blink(intervalMs = BLINK_INTERVAL_MS) { + if (this._current === 'blink') return; + this._stopBlink(); + this._blinkState = true; + this._write(1); + this._blinkTimer = setInterval(() => { + this._blinkState = !this._blinkState; + this._write(this._blinkState ? 1 : 0); + }, intervalMs); + this._current = 'blink'; + log.info('led', 'BT 指示灯 → 闪烁'); + } + + /** 释放资源,关灯 */ + destroy() { + this._stopBlink(); + this._write(0); + this._current = 'off'; + } + + _stopBlink() { + if (this._blinkTimer) { + clearInterval(this._blinkTimer); + this._blinkTimer = null; + } + } + + _write(val) { + try { + fs.writeFileSync(BT_LED_PATH, String(val)); + } catch (e) { + log.warn('led', `写入失败 (${BT_LED_PATH}): ${e.message}`); + } + } +} + // ── VFD 显示屏 ──────────────────────────────────────────────────────────────── const DISPLAY_PATH = '/sys/devices/platform/openvfd/attr/led'; @@ -207,5 +283,6 @@ class StatusLed { // 全局单例,整个进程共用 module.exports = new WifiLed(); +module.exports.bt = new BtLed(); module.exports.status = new StatusLed(); module.exports.display = new Display(); diff --git a/lib/provisioning.js b/lib/provisioning.js index 72b3c7f..b5439d3 100644 --- a/lib/provisioning.js +++ b/lib/provisioning.js @@ -35,12 +35,14 @@ class ProvisionManager extends EventEmitter { isApMode() { return this._state === 'ap'; } async start() { - led.off(); // 初始状态:灭灯 + led.off(); // WiFi 灯初始状态:熄灭 + led.bt.off(); // BT 灯初始状态:熄灭 // WiFi STA 已连接 → 直接进入 STA 模式 if (isWifiStaConnected()) { this._state = 'sta'; log.info('provision', 'WiFi STA 已连接,AP 不启动'); + led.bt.on(); // 已配网,BT 灯常亮 this._emitNetworkReady(); this._startMonitor(); return; @@ -60,11 +62,13 @@ class ProvisionManager extends EventEmitter { // 无网:有已保存的 WiFi 配置 → 等 NM 自动连接(重启场景) if (hasSavedWifiConnection()) { log.info('provision', '发现已保存的 WiFi 配置,等待 NetworkManager 自动连接...'); - led.blink(); // 等待自动重连期间闪烁 + led.blink(); // WiFi 灯:等待自动重连期间闪烁 + led.bt.blink(); // BT 灯:等待配网期间闪烁 const connected = await this._waitForWifiConnect(); if (connected) { this._state = 'sta'; log.info('provision', 'WiFi 自动连接成功,AP 不启动'); + led.bt.on(); // 自动重连成功,BT 灯常亮 this._emitNetworkReady(); this._startMonitor(); return; @@ -114,7 +118,8 @@ class ProvisionManager extends EventEmitter { this._stopMonitor(); this._stopAll(); this._state = 'idle'; - led.destroy(); // 停止时关灯、释放闪烁定时器 + led.destroy(); // WiFi 灯:停止时关灯、释放闪烁定时器 + led.bt.destroy(); // BT 灯:停止时关灯 } // ── 进入 AP 模式 ───────────────────────────────────────────────────────── @@ -122,7 +127,8 @@ class ProvisionManager extends EventEmitter { _enterAP() { if (this._state === 'ap') return; - led.off(); // AP 模式:WiFi 未连接,灭灯 + led.off(); // AP 模式:WiFi 未连接,WiFi 灯熄灭 + led.bt.blink(); // AP 模式:BLE 配网进行中,BT 灯闪烁 if (!hasInternet()) led.display.showAP(); // 无网时立即显示 AP,有线时等 WS 连接后再定 try { @@ -165,16 +171,17 @@ class ProvisionManager extends EventEmitter { const result = connectWifi(ssid, password); - if (result.success) { + if (result.success) { this._state = 'sta'; log.info('provision', `WiFi 已连接: ${ssid}`); - led.on(); // 连接成功 → 常亮 + led.on(); // WiFi 灯:连接成功 → 常亮 + led.bt.on(); // BT 灯:配网成功 → 常亮 this.emit('network-ready'); return result; } log.warn('provision', `WiFi 连接失败: ${result.error},重新启动 AP`); - this._enterAP(); // _enterAP 内部会调用 led.off() + this._enterAP(); // _enterAP 内部会调用 led.off() / led.bt.blink() return result; } @@ -196,6 +203,7 @@ class ProvisionManager extends EventEmitter { log.info('provision', 'WiFi 已外部连接,关闭 AP'); this._stopAPServices(); this._state = 'sta'; + led.bt.on(); // 外部连接成功,BT 灯常亮 this.emit('network-ready'); } @@ -203,11 +211,13 @@ class ProvisionManager extends EventEmitter { if (this._state === 'sta') { if (hasInternet()) { led.on(); + led.bt.on(); // 有网 → BT 灯常亮 } else { - led.off(); // WiFi 已连接但无互联网 + led.off(); // WiFi 已连接但无互联网 + led.bt.off(); // 无互联网时 BT 灯也熄灭 } } - // AP 模式下 led 已在 _enterAP() 中熄灭,无需重复操作 + // AP 模式下 led/bt 已在 _enterAP() 中设置,无需重复操作 }, MONITOR_INTERVAL_MS); }