diff --git a/lib/captive-server.js b/lib/captive-server.js index 4bcd6a2..c31cde5 100644 --- a/lib/captive-server.js +++ b/lib/captive-server.js @@ -124,7 +124,9 @@ class CaptiveServer { // 延迟执行,确保 HTTP 响应送达 if (this._onConnect) { setTimeout(() => { - this._onConnect(ssid, password || ''); + Promise.resolve(this._onConnect(ssid, password || '')).catch((e) => { + log.error('http', '配网回调异常:', e.message); + }); }, 1000); } } diff --git a/lib/network.js b/lib/network.js index c66a151..29d4e69 100644 --- a/lib/network.js +++ b/lib/network.js @@ -193,8 +193,12 @@ function scanWifi() { } } +/** AP 切 STA 后等待网卡进入 connected 的最长时间(不依赖外网探测) */ +const CONNECT_WIFI_STA_WAIT_MS = 25_000; +const CONNECT_WIFI_STA_POLL_MS = 1_000; + /** - * 连接指定 WiFi + * 连接指定 WiFi(配网场景:成功 = NM 显示 STA 已连上目标网,不要求一定能 ping 通 8.8.8.8) * @returns {{ success: boolean, error?: string }} */ function connectWifi(ssid, password) { @@ -205,15 +209,24 @@ function connectWifi(ssid, password) { try { run(`nmcli connection delete "${ssid}"`); } catch (_) {} const pwdArg = password ? `password "${password}"` : ''; - run(`nmcli device wifi connect "${ssid}" ${pwdArg} ifname ${iface}`, 30000); + run(`nmcli device wifi connect "${ssid}" ${pwdArg} ifname ${iface}`, 60000); - // 验证连通性 - sleep(3000); - if (hasInternet()) { - log.info('network', `WiFi 已连接: ${ssid}`); - return { success: true }; + const deadline = Date.now() + CONNECT_WIFI_STA_WAIT_MS; + while (Date.now() < deadline) { + if (isWifiStaConnected()) { + if (hasInternet()) { + log.info('network', `WiFi 已连接且有外网: ${ssid}`); + } else { + log.warn( + 'network', + `WiFi STA 已连接(${ssid}),暂未检测到外网;配网仍视为成功(内网/防火墙/国内 DNS 常见)`, + ); + } + return { success: true }; + } + sleep(CONNECT_WIFI_STA_POLL_MS); } - return { success: false, error: '已连接但无法访问互联网' }; + return { success: false, error: '超时:网卡未进入已连接状态' }; } catch (e) { log.error('network', `WiFi 连接失败: ${e.message}`); return { success: false, error: e.message }; @@ -308,8 +321,12 @@ function isWifiStaConnected() { const out = run('nmcli -t -f DEVICE,TYPE,STATE,CONNECTION device'); for (const line of out.split('\n')) { const parts = line.split(':'); - if (parts[0] === iface && parts[1] === 'wifi' && parts[2] === 'connected') { - return parts[3] !== CON_NAME; + const dev = (parts[0] || '').trim(); + const type = (parts[1] || '').trim(); + const state = (parts[2] || '').trim(); + const conn = (parts[3] || '').trim(); + if (dev === iface && type === 'wifi' && state === 'connected') { + return conn !== CON_NAME; } } } catch (_) {} diff --git a/lib/provisioning.js b/lib/provisioning.js index 541da3f..292e060 100644 --- a/lib/provisioning.js +++ b/lib/provisioning.js @@ -163,6 +163,9 @@ class ProvisionManager extends EventEmitter { this._stopAPServices(); + // 关热点后射频/模式切换需要时间,立刻 connect 在部分板子上会失败 + await new Promise((r) => setTimeout(r, 2500)); + const result = connectWifi(ssid, password); if (result.success) {