feat(rk3588s): unify display state semantics and bump version to 1.4.5

This commit is contained in:
2026-05-23 18:22:13 +08:00
parent 5347a728da
commit 161e0e654c
6 changed files with 390 additions and 377 deletions

View File

@@ -36,19 +36,19 @@ class StatusLed {
class Display { class Display {
showAP() { showAP() {
if (writeLvglCommand('show_text:AP')) { if (writeLvglCommand('show_ap')) {
log.info('display', '显示屏 → AP'); log.info('display', '显示屏 → AP(闪烁)');
} }
} }
showConn() { showConn() {
if (writeLvglCommand('show_text:Conn')) { if (writeLvglCommand('show_conn')) {
log.info('display', '显示屏 → Conn'); log.info('display', '显示屏 → Conn(闪烁)');
} }
} }
showErr0() { showErr0() {
if (writeLvglCommand('show_text:Err0')) { if (writeLvglCommand('show_err0')) {
log.info('display', '显示屏 → Err0'); log.info('display', '显示屏 → Err0');
} }
} }
@@ -61,8 +61,8 @@ class Display {
showPin(pin) { showPin(pin) {
const s = String(pin || '').padStart(4, '0').slice(-4); const s = String(pin || '').padStart(4, '0').slice(-4);
if (writeLvglCommand(`show_text:${s}`)) { if (writeLvglCommand(`show_pin:${s}`)) {
log.info('display', `显示屏 → PIN: ${s}`); log.info('display', `显示屏 → PIN: ${s}(闪烁)`);
} }
} }
} }

View File

@@ -1,360 +1,360 @@
'use strict'; 'use strict';
const EventEmitter = require('events'); const EventEmitter = require('events');
const log = require('./logger'); const log = require('./logger');
const { hasInternet, hasSavedWifiConnection, connectSavedWifiConnections, isWifiStaConnected, scanWifi, startAP, stopAP, connectWifi, getWifiIface, AP_IP } = require('./network'); const { hasInternet, hasSavedWifiConnection, connectSavedWifiConnections, isWifiStaConnected, scanWifi, startAP, stopAP, connectWifi, getWifiIface, AP_IP } = require('./network');
const { DnsHijack } = require('./dns-hijack'); const { DnsHijack } = require('./dns-hijack');
const { CaptiveServer } = require('./captive-server'); const { CaptiveServer } = require('./captive-server');
const led = require('./led'); const led = require('./led');
const MONITOR_INTERVAL_MS = 15_000; const MONITOR_INTERVAL_MS = 15_000;
const WIFI_RECONNECT_MAX_ROUNDS = 3; const WIFI_RECONNECT_MAX_ROUNDS = 3;
const WIFI_RECONNECT_ROUND_DELAY_MS = 5_000; const WIFI_RECONNECT_ROUND_DELAY_MS = 5_000;
const AP_SAVED_WIFI_RETRY_INTERVAL_MS = 180_000; const AP_SAVED_WIFI_RETRY_INTERVAL_MS = 180_000;
const AP_MIN_UP_BEFORE_RETRY_MS = 60_000; const AP_MIN_UP_BEFORE_RETRY_MS = 60_000;
/** /**
* AP 常驻配网管理器。 * AP 常驻配网管理器。
* *
* 规则: * 规则:
* - 启动时WiFi STA 优先;有已保存 WiFi 时主动让 NM 重连,最多 3 轮 * - 启动时WiFi STA 优先;有已保存 WiFi 时主动让 NM 重连,最多 3 轮
* - 有线网络可用时:通知网络就绪,但不自动开启 AP * - 有线网络可用时:通知网络就绪,但不自动开启 AP
* - 自动开 AP 的唯一兜底:无有线/无 WiFi且无 saved WiFi 或 saved WiFi 3 轮失败 * - 自动开 AP 的唯一兜底:无有线/无 WiFi且无 saved WiFi 或 saved WiFi 3 轮失败
* - 用户提交 WiFi 凭证 → 关 AP → 尝试连接 → 失败则按网络状态决定是否重新开 AP * - 用户提交 WiFi 凭证 → 关 AP → 尝试连接 → 失败则按网络状态决定是否重新开 AP
* - AP 状态下:若仍无有线网络,低频释放 wlan0 并尝试 saved WiFi * - AP 状态下:若仍无有线网络,低频释放 wlan0 并尝试 saved WiFi
*/ */
class ProvisionManager extends EventEmitter { class ProvisionManager extends EventEmitter {
constructor(clawId) { constructor(clawId) {
super(); super();
this._clawId = clawId || 'Setup'; this._clawId = clawId || 'Setup';
this._state = 'idle'; // 'idle' | 'ap' | 'connecting' | 'sta' | 'wired' this._state = 'idle'; // 'idle' | 'ap' | 'connecting' | 'sta' | 'wired'
this._dns = null; this._dns = null;
this._server = null; this._server = null;
this._monitorTimer = null; this._monitorTimer = null;
this._monitorBusy = false; this._monitorBusy = false;
this._apStartedAt = 0; this._apStartedAt = 0;
this._lastApSavedWifiRetryAt = 0; this._lastApSavedWifiRetryAt = 0;
} }
/** 是否正处于 AP 模式WiFi 热点广播中) */ /** 是否正处于 AP 模式WiFi 热点广播中) */
isApMode() { return this._state === 'ap'; } isApMode() { return this._state === 'ap'; }
async start() { async start() {
led.off(); // WiFi 灯初始状态:熄灭 led.off(); // WiFi 灯初始状态:熄灭
// WiFi STA 已连接 → 直接进入 STA 模式 // WiFi STA 已连接 → 直接进入 STA 模式
if (isWifiStaConnected()) { if (isWifiStaConnected()) {
this._state = 'sta'; this._state = 'sta';
log.info('provision', 'WiFi STA 已连接AP 不启动'); log.info('provision', 'WiFi STA 已连接AP 不启动');
this._emitNetworkReady(); this._emitNetworkReady();
this._startMonitor(); this._startMonitor();
return; return;
} }
// 网络已就绪时先启动 WShasInternet() 可能来自 WiFi也可能来自有线不能直接当作 wired。 // 网络已就绪时先启动 WShasInternet() 可能来自 WiFi也可能来自有线不能直接当作 wired。
if (hasInternet()) { if (hasInternet()) {
if (isWifiStaConnected()) { if (isWifiStaConnected()) {
this._state = 'sta'; this._state = 'sta';
log.info('provision', 'WiFi STA 已连接AP 不启动'); log.info('provision', 'WiFi STA 已连接AP 不启动');
this._emitNetworkReady(); this._emitNetworkReady();
} else { } else {
this._state = 'wired'; this._state = 'wired';
log.info('provision', '有线网络就绪,启动 WS不自动开启 AP'); log.info('provision', '有线网络就绪,启动 WS不自动开启 AP');
led.off(); led.off();
this._emitNetworkReady(); this._emitNetworkReady();
} }
this._startMonitor(); this._startMonitor();
return; return;
} }
// 无有线可用时,有 saved WiFi 才主动让 NetworkManager 重连;不要只被动等待 NM autoconnect。 // 无有线可用时,有 saved WiFi 才主动让 NetworkManager 重连;不要只被动等待 NM autoconnect。
if (hasSavedWifiConnection()) { if (hasSavedWifiConnection()) {
log.info('provision', `发现已保存的 WiFi 配置,主动重连(最多 ${WIFI_RECONNECT_MAX_ROUNDS} 轮)...`); log.info('provision', `发现已保存的 WiFi 配置,主动重连(最多 ${WIFI_RECONNECT_MAX_ROUNDS} 轮)...`);
this._state = 'connecting'; this._state = 'connecting';
led.blink(); led.blink();
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS); const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
if (connected) { if (connected) {
this._state = 'sta'; this._state = 'sta';
log.info('provision', '已保存 WiFi 重连成功AP 不启动'); log.info('provision', '已保存 WiFi 重连成功AP 不启动');
this._emitNetworkReady(); this._emitNetworkReady();
this._startMonitor(); this._startMonitor();
return; return;
} }
log.warn('provision', '已保存 WiFi 重连失败'); log.warn('provision', '已保存 WiFi 重连失败');
} }
// 无有线、无 WiFi且无 saved WiFi 或 saved WiFi 3 轮失败 → 开 AP 兜底配网。 // 无有线、无 WiFi且无 saved WiFi 或 saved WiFi 3 轮失败 → 开 AP 兜底配网。
this._enterAP(); this._enterAP();
this._startMonitor(); this._startMonitor();
} }
_emitNetworkReady() { _emitNetworkReady() {
if (hasInternet()) { if (hasInternet()) {
// WiFi 灯只在 STA 模式下亮(有线有网而 WiFi 在 AP 模式时不亮) // WiFi 灯只在 STA 模式下亮(有线有网而 WiFi 在 AP 模式时不亮)
if (this._state === 'sta') led.on(); if (this._state === 'sta') led.on();
this.emit('network-ready'); this.emit('network-ready');
} else { } else {
log.warn('provision', 'hasInternet() 返回 falseLED 保持熄灭'); log.warn('provision', 'hasInternet() 返回 falseLED 保持熄灭');
} }
} }
async _trySavedWifiReconnectRounds(rounds = WIFI_RECONNECT_MAX_ROUNDS) { async _trySavedWifiReconnectRounds(rounds = WIFI_RECONNECT_MAX_ROUNDS) {
for (let i = 1; i <= rounds; i++) { for (let i = 1; i <= rounds; i++) {
if (isWifiStaConnected()) return true; if (isWifiStaConnected()) return true;
log.info('provision', `尝试已保存 WiFi 重连:第 ${i}/${rounds}`); log.info('provision', `尝试已保存 WiFi 重连:第 ${i}/${rounds}`);
const result = await connectSavedWifiConnections(); const result = await connectSavedWifiConnections();
if (result.success || isWifiStaConnected()) return true; if (result.success || isWifiStaConnected()) return true;
if (i < rounds) { if (i < rounds) {
await new Promise((r) => setTimeout(r, WIFI_RECONNECT_ROUND_DELAY_MS)); await new Promise((r) => setTimeout(r, WIFI_RECONNECT_ROUND_DELAY_MS));
} }
} }
return false; return false;
} }
stop() { stop() {
this._stopMonitor(); this._stopMonitor();
this._stopAll(); this._stopAll();
this._state = 'idle'; this._state = 'idle';
led.destroy(); // WiFi 灯:停止时关灯、释放闪烁定时器 led.destroy(); // WiFi 灯:停止时关灯、释放闪烁定时器
} }
// ── 进入 AP 模式 ───────────────────────────────────────────────────────── // ── 进入 AP 模式 ─────────────────────────────────────────────────────────
_enterAP() { _enterAP() {
if (this._state === 'ap') return; if (this._state === 'ap') return;
led.off(); // AP 模式WiFi 未连接WiFi 灯熄灭 led.off(); // AP 模式WiFi 未连接WiFi 灯熄灭
if (!hasInternet()) led.display.showAP(); // 无网时立即显示 AP有线时等 WS 连接后再定
try {
try { // 若上次进程退出前留下 clawd-hotspot必须先释放 wlan0否则会在 AP 模式下扫描,列表可能只剩 2.4G/自身热点。
// 若上次进程退出前留下 clawd-hotspot必须先释放 wlan0否则会在 AP 模式下扫描,列表可能只剩 2.4G/自身热点。 stopAP();
stopAP();
// AP 模式下无法扫描 WiFi必须在开 AP 之前扫描并缓存
// AP 模式下无法扫描 WiFi必须在开 AP 之前扫描并缓存 log.info('provision', '扫描周边 WiFi...');
log.info('provision', '扫描周边 WiFi...'); this._cachedWifiList = scanWifi();
this._cachedWifiList = scanWifi(); log.info('provision', `扫描到 ${this._cachedWifiList.length} 个网络: ${this._cachedWifiList.map(w => `${w.ssid}${w.band ? `(${w.band})` : ''}`).join(', ')}`);
log.info('provision', `扫描到 ${this._cachedWifiList.length} 个网络: ${this._cachedWifiList.map(w => `${w.ssid}${w.band ? `(${w.band})` : ''}`).join(', ')}`);
// 写 DNS 劫持配置NM 启动热点时加载);接口名与热点一致,勿写死 wlan0
// 写 DNS 劫持配置NM 启动热点时加载);接口名与热点一致,勿写死 wlan0 this._dns = new DnsHijack();
this._dns = new DnsHijack(); this._dns.start(getWifiIface(), AP_IP);
this._dns.start(getWifiIface(), AP_IP);
const ap = startAP(this._clawId);
const ap = startAP(this._clawId);
this._server = new CaptiveServer({
this._server = new CaptiveServer({ clawId: this._clawId,
clawId: this._clawId, cachedWifiList: this._cachedWifiList,
cachedWifiList: this._cachedWifiList, onConnect: (ssid, password) => this._handleWifiConnect(ssid, password),
onConnect: (ssid, password) => this._handleWifiConnect(ssid, password), });
}); this._server.startListening();
this._server.startListening();
this._state = 'ap';
this._state = 'ap'; led.display.showAP();
this._apStartedAt = Date.now(); this._apStartedAt = Date.now();
this._lastApSavedWifiRetryAt = 0; this._lastApSavedWifiRetryAt = 0;
log.info('provision', `AP 常驻模式已启动: ${ap.ssid}, 密码 12345678`); log.info('provision', `AP 常驻模式已启动: ${ap.ssid}, 密码 12345678`);
log.info('provision', `配网地址: http://10.42.0.1`); log.info('provision', `配网地址: http://10.42.0.1`);
} catch (e) { } catch (e) {
log.error('provision', `AP 启动失败: ${e.message}`); log.error('provision', `AP 启动失败: ${e.message}`);
if (this._state !== 'sta') this._state = 'idle'; if (this._state !== 'sta') this._state = 'idle';
} }
} }
// ── 用户提交 WiFi 凭证 ─────────────────────────────────────────────────── // ── 用户提交 WiFi 凭证 ───────────────────────────────────────────────────
async _handleWifiConnect(ssid, password) { async _handleWifiConnect(ssid, password) {
if (this._state === 'connecting') return { success: false, error: '正在连接中,请稍候' }; if (this._state === 'connecting') return { success: false, error: '正在连接中,请稍候' };
this._state = 'connecting'; this._state = 'connecting';
log.info('provision', `用户请求连接 WiFi: ${ssid}`); log.info('provision', `用户请求连接 WiFi: ${ssid}`);
led.blink(); // 正在连接 → 闪烁 led.blink(); // 正在连接 → 闪烁
try { try {
this._stopAPServices(); this._stopAPServices();
// 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败 // 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败
await new Promise((r) => setTimeout(r, 3500)); await new Promise((r) => setTimeout(r, 3500));
const result = await connectWifi(ssid, password); const result = await connectWifi(ssid, password);
if (result.success) { if (result.success) {
this._state = 'sta'; this._state = 'sta';
log.info('provision', `WiFi 已连接: ${ssid}`); log.info('provision', `WiFi 已连接: ${ssid}`);
led.on(); // WiFi 灯:连接成功 → 常亮 led.on(); // WiFi 灯:连接成功 → 常亮
this.emit('network-ready'); this.emit('network-ready');
return result; return result;
} }
log.warn('provision', `WiFi 连接失败: ${result.error},按当前网络状态恢复`); log.warn('provision', `WiFi 连接失败: ${result.error},按当前网络状态恢复`);
this._recoverAfterWifiFailure(); this._recoverAfterWifiFailure();
return result; return result;
} catch (e) { } catch (e) {
log.error('provision', `配网过程异常: ${e.message}`); log.error('provision', `配网过程异常: ${e.message}`);
this._recoverAfterWifiFailure(); this._recoverAfterWifiFailure();
return { success: false, error: e.message }; return { success: false, error: e.message };
} }
} }
/** WiFi 连接失败后:有线可用则保持 wired否则开 AP 兜底。 */ /** WiFi 连接失败后:有线可用则保持 wired否则开 AP 兜底。 */
_recoverAfterWifiFailure() { _recoverAfterWifiFailure() {
if (hasInternet()) { if (hasInternet()) {
this._state = 'wired'; this._state = 'wired';
led.off(); led.off();
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
this._safeReenterAP(); this._safeReenterAP();
} }
/** 重新开 AP失败时勿把 _state 永久卡在 connecting */ /** 重新开 AP失败时勿把 _state 永久卡在 connecting */
_safeReenterAP() { _safeReenterAP() {
try { try {
this._enterAP(); this._enterAP();
} catch (e) { } catch (e) {
log.error('provision', `重新启动 AP 失败: ${e.message}`); log.error('provision', `重新启动 AP 失败: ${e.message}`);
this._state = 'idle'; this._state = 'idle';
} }
} }
// ── WiFi 状态监控 ───────────────────────────────────────────────────────── // ── WiFi 状态监控 ─────────────────────────────────────────────────────────
_startMonitor() { _startMonitor() {
this._monitorTimer = setInterval(() => { this._monitorTimer = setInterval(() => {
if (this._monitorBusy) return; if (this._monitorBusy) return;
this._monitorBusy = true; this._monitorBusy = true;
this._monitorTick() this._monitorTick()
.catch((e) => log.error('provision', `WiFi 状态监控异常: ${e.message}`)) .catch((e) => log.error('provision', `WiFi 状态监控异常: ${e.message}`))
.finally(() => { this._monitorBusy = false; }); .finally(() => { this._monitorBusy = false; });
}, MONITOR_INTERVAL_MS); }, MONITOR_INTERVAL_MS);
} }
async _monitorTick() { async _monitorTick() {
if (this._state === 'connecting') return; if (this._state === 'connecting') return;
const wifiUp = isWifiStaConnected(); const wifiUp = isWifiStaConnected();
if (wifiUp && this._state !== 'sta') { if (wifiUp && this._state !== 'sta') {
if (this._state === 'ap') { if (this._state === 'ap') {
log.info('provision', 'WiFi 已外部连接,关闭 AP'); log.info('provision', 'WiFi 已外部连接,关闭 AP');
this._stopAPServices(); this._stopAPServices();
} }
this._state = 'sta'; this._state = 'sta';
this._emitNetworkReady(); this._emitNetworkReady();
} }
if (this._state === 'sta' && !wifiUp) { if (this._state === 'sta' && !wifiUp) {
log.warn('provision', 'WiFi 连接已断开,尝试恢复网络'); log.warn('provision', 'WiFi 连接已断开,尝试恢复网络');
await this._recoverNetworkWithoutWifi(); await this._recoverNetworkWithoutWifi();
return; return;
} }
if (this._state === 'wired') { if (this._state === 'wired') {
if (!hasInternet()) { if (!hasInternet()) {
log.warn('provision', '有线网络不可用,尝试恢复 WiFi'); log.warn('provision', '有线网络不可用,尝试恢复 WiFi');
await this._recoverNetworkWithoutWifi(); await this._recoverNetworkWithoutWifi();
return; return;
} }
led.off(); led.off();
return; return;
} }
if (this._state === 'ap') { if (this._state === 'ap') {
if (hasInternet()) { if (hasInternet()) {
log.info('provision', '检测到有线网络可用,关闭 AP'); log.info('provision', '检测到有线网络可用,关闭 AP');
this._stopAPServices(); this._stopAPServices();
this._state = 'wired'; this._state = 'wired';
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
if (hasSavedWifiConnection() && this._shouldRetrySavedWifiFromAP()) { if (hasSavedWifiConnection() && this._shouldRetrySavedWifiFromAP()) {
await this._retrySavedWifiFromAP(); await this._retrySavedWifiFromAP();
return; return;
} }
led.off(); led.off();
return; return;
} }
} }
async _recoverNetworkWithoutWifi() { async _recoverNetworkWithoutWifi() {
this._state = 'connecting'; this._state = 'connecting';
led.blink(); led.blink();
if (hasSavedWifiConnection()) { if (hasSavedWifiConnection()) {
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS); const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
if (connected) { if (connected) {
this._state = 'sta'; this._state = 'sta';
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
} }
if (hasInternet()) { if (hasInternet()) {
this._state = 'wired'; this._state = 'wired';
led.off(); led.off();
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
this._safeReenterAP(); this._safeReenterAP();
} }
_shouldRetrySavedWifiFromAP() { _shouldRetrySavedWifiFromAP() {
const now = Date.now(); const now = Date.now();
if (this._apStartedAt && now - this._apStartedAt < AP_MIN_UP_BEFORE_RETRY_MS) return false; if (this._apStartedAt && now - this._apStartedAt < AP_MIN_UP_BEFORE_RETRY_MS) return false;
if (this._lastApSavedWifiRetryAt && now - this._lastApSavedWifiRetryAt < AP_SAVED_WIFI_RETRY_INTERVAL_MS) return false; if (this._lastApSavedWifiRetryAt && now - this._lastApSavedWifiRetryAt < AP_SAVED_WIFI_RETRY_INTERVAL_MS) return false;
return true; return true;
} }
async _retrySavedWifiFromAP() { async _retrySavedWifiFromAP() {
this._lastApSavedWifiRetryAt = Date.now(); this._lastApSavedWifiRetryAt = Date.now();
log.info('provision', 'AP 模式下定期尝试已保存 WiFi'); log.info('provision', 'AP 模式下定期尝试已保存 WiFi');
this._state = 'connecting'; this._state = 'connecting';
led.blink(); led.blink();
this._stopAPServices(); this._stopAPServices();
await new Promise((r) => setTimeout(r, 3500)); await new Promise((r) => setTimeout(r, 3500));
const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS); const connected = await this._trySavedWifiReconnectRounds(WIFI_RECONNECT_MAX_ROUNDS);
if (connected) { if (connected) {
this._state = 'sta'; this._state = 'sta';
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
if (hasInternet()) { if (hasInternet()) {
this._state = 'wired'; this._state = 'wired';
led.off(); led.off();
this._emitNetworkReady(); this._emitNetworkReady();
return; return;
} }
log.warn('provision', 'AP 模式下重试已保存 WiFi 失败,恢复 AP'); log.warn('provision', 'AP 模式下重试已保存 WiFi 失败,恢复 AP');
this._safeReenterAP(); this._safeReenterAP();
} }
_stopMonitor() { _stopMonitor() {
if (this._monitorTimer) { if (this._monitorTimer) {
clearInterval(this._monitorTimer); clearInterval(this._monitorTimer);
this._monitorTimer = null; this._monitorTimer = null;
} }
} }
// ── 清理 ────────────────────────────────────────────────────────────────── // ── 清理 ──────────────────────────────────────────────────────────────────
_stopAPServices() { _stopAPServices() {
if (this._server) { if (this._server) {
this._server.stop(); this._server.stop();
this._server = null; this._server = null;
} }
if (this._dns) { if (this._dns) {
this._dns.stop(); this._dns.stop();
this._dns = null; this._dns = null;
} }
stopAP(); stopAP();
} }
_stopAll() { _stopAll() {
this._stopAPServices(); this._stopAPServices();
} }
} }
module.exports = { ProvisionManager }; module.exports = { ProvisionManager };

Binary file not shown.

View File

@@ -142,7 +142,6 @@ static void apply_show_text_async(void *arg)
{ {
const char *text = (const char *)arg; const char *text = (const char *)arg;
show_custom_text = true; show_custom_text = true;
custom_text_blink = false;
custom_text_visible = true; custom_text_visible = true;
lv_label_set_text(custom_text_label, text ? text : ""); lv_label_set_text(custom_text_label, text ? text : "");
lv_obj_align(custom_text_label, LV_ALIGN_CENTER, 0, -25); lv_obj_align(custom_text_label, LV_ALIGN_CENTER, 0, -25);
@@ -201,14 +200,28 @@ static void *fifo_thread(void *arg)
line[--len] = '\0'; line[--len] = '\0';
} }
if (strncmp(line, "show_text:", 10) == 0) { if (strcmp(line, "show_ap") == 0) {
lv_async_call(apply_show_ap_async, NULL);
} else if (strcmp(line, "show_conn") == 0) {
strncpy(custom_text_buf, "Conn", sizeof(custom_text_buf) - 1);
custom_text_buf[sizeof(custom_text_buf) - 1] = '\0';
custom_text_blink = true;
lv_async_call(apply_show_text_async, custom_text_buf);
} else if (strcmp(line, "show_err0") == 0) {
strncpy(custom_text_buf, "Err0", sizeof(custom_text_buf) - 1);
custom_text_buf[sizeof(custom_text_buf) - 1] = '\0';
custom_text_blink = false;
lv_async_call(apply_show_text_async, custom_text_buf);
} else if (strncmp(line, "show_pin:", 9) == 0) {
strncpy(custom_text_buf, line + 9, sizeof(custom_text_buf) - 1);
custom_text_buf[sizeof(custom_text_buf) - 1] = '\0';
custom_text_blink = true;
lv_async_call(apply_show_text_async, custom_text_buf);
} else if (strncmp(line, "show_text:", 10) == 0) {
strncpy(custom_text_buf, line + 10, sizeof(custom_text_buf) - 1); strncpy(custom_text_buf, line + 10, sizeof(custom_text_buf) - 1);
custom_text_buf[sizeof(custom_text_buf) - 1] = '\0'; custom_text_buf[sizeof(custom_text_buf) - 1] = '\0';
if (strcmp(custom_text_buf, "AP") == 0) { custom_text_blink = false;
lv_async_call(apply_show_ap_async, NULL); lv_async_call(apply_show_text_async, custom_text_buf);
} else {
lv_async_call(apply_show_text_async, custom_text_buf);
}
} else if (strcmp(line, "show_time") == 0) { } else if (strcmp(line, "show_time") == 0) {
lv_async_call(apply_show_time_async, NULL); lv_async_call(apply_show_time_async, NULL);
} }

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{ {
"name": "clawd", "name": "clawd",
"version": "1.4.4", "version": "1.4.5",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "clawd", "name": "clawd",
"version": "1.4.4", "version": "1.4.5",
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"ssh2": "^1.17.0", "ssh2": "^1.17.0",

View File

@@ -1,6 +1,6 @@
{ {
"name": "clawd", "name": "clawd",
"version": "1.4.4", "version": "1.4.5",
"description": "Claw Box daemon - connects local Linux box to claw.cutos.ai via WebSocket", "description": "Claw Box daemon - connects local Linux box to claw.cutos.ai via WebSocket",
"main": "lib/client.js", "main": "lib/client.js",
"bin": { "bin": {