单台 CM4 中继盒的设置页(TAPD #1003131),从仪表盘卡片头部的齿轮图标进入,按 deviceId 而不是"当前设备"操作——同一账号下用户可能在不同地点各有一台中继盒,需要分别配置。Per Liang 2026-06-02 的最终范围(把 C/E/F 那几项收回全局 Settings 页之后):连接模式(BT/WiFi)、WiFi 网络(重新)配置、蜂鸣器模式、显示模式、背光亮度、固件升级、解绑,一共 7 项。
补充:WiFi 切换链路在 mqttConnected == false 的补救路径中还依赖 accountMqttCredServiceProvider。_resendMqttConfig() 会先尝试为当前 deviceId 复用最近 mint 的设备 MQTT 凭证(reuseRecent: true),而不是再次轮换;若当前拿不到可用凭证,则跳过 M_ID / M_PD,最后重发 HOST。这意味着该页不只依赖连接管理与 BLE/region/auth provider,也直接依赖账号侧的 MQTT 凭证服务。
主文件:lib/features/thermometer/presentation/pages/booster_configurations_page.dart(1001 行)
Provider:connectedBoostersProvider(离开列表自动 pop)、buzzerRingModeProvider / boosterDisplayModeProvider / boosterBacklightLevelProvider(三个本地持久化的乐观 pick;Buzzer 与 Backlight 的值为 int,Display mode 的值为 String)、deviceConnectionManagerProvider、bleDeviceServiceProvider、authControllerProvider(WiFi 模式的登录闸)、boosterProvider、regionServiceProvider
入参:deviceId(路由 arguments,裸字符串)
/booster-config(BoosterConfigurationsPage.routeName)lib/app.dart 的 onGenerateRoute顶部大号 deviceId 标题,下方分两组:
Connection 区的 Connection Mode 与 WiFi network 两行不是无条件可点。代码用 bleLinked(当前手机是否持有这台设备的原始 BLE/GATT 链路)控制它们的 enabled 状态;若这台手机只是通过云端连到该设备,这两行会以禁用态显示,点按时不会进入选择 sheet 或配网页,而是弹出 boosterConfigNeedsBleBody 说明提示。
ref.listen(connectedBoostersProvider) 全程盯着这台设备:一旦它从已连接列表消失(刚被解绑、或掉线太久被清除),本页自动 pop,避免用户停留在一个已经不存在的设备的设置页上。
isWifiCloudMode(deviceId) 与 connectedBoostersProvider.notifier.cloudLiveDeviceIds.contains(deviceId);当 manager 的 sticky pin 落后于真实云端会话时,后者作为 ground truth。switchToBle(deviceId),只把当前这台手机上的 App 通道切回 BLE;当前实现不会再向中继盒发送 WIFI=0,因此中继盒自己的 WiFi/broker 链路会继续保持,账号下其他手机仍可继续走 MQTT。_selectWifiMode):未登录(TAPD #1003350)→ 跳 SignInLandingPage(带 deviceId),"连接成功"绝不能在未登录状态下出现
未曾配网 → 直接跳 WifiNetworkSelectPage(重新走 WiFi 配置流程)
已配网 → 先经 BLE 发 WIFI=1 唤醒中继盒 WiFi(TAPD #1003327,之前若切过 Bluetooth 会把 WiFi 关掉)
→ _verifySwitchAndConfirm():
1. 快路径(TAPD #1003370):app 自己先切云端(switchToCloud),8 秒内等 App 自己的
云会话收到第一帧(cloudLiveDeviceIds,TAPD #1003364)——这一帧证明 WiFi+broker+app
全链路通了,比等中继盒自己那条约 10 秒一推的 WIFI_STS/MQTT_STS BLE 状态字节快得多
2. 8 秒内没等到 → 退回 BLE 诊断:14 秒内等 WIFI_STS 变为非 disconnected;仍是 0 →
判定原存的 WiFi 从未(重新)连上(TAPD #1003358),送回 WifiNetworkSelectPage
3. WiFi 已通但 `mqttConnected` 仍是 false → 进入 `_resendMqttConfig()`:先尝试 `accountMqttCredServiceProvider.mintDeviceCred(deviceId)`;若拿到新凭证,则重发 `M_ID`、`M_PD`,最后重发 `HOST`;若 mint 不可用(guest/offline),则跳过 `M_ID/M_PD`,只重发 `HOST`,再给 12 秒等云端确认
4. 三种结局:confirmed(成功提示) / needsWifiSetup(送回配网页) /
failed(退回 Bluetooth,提示"无法连接云端";不会额外发 `WIFI=0`)
context.mounted 守卫(TAPD #1003274:中继盒若在等待期间掉线,ref.listen 会先一步 pop 掉本页,此时继续用已销毁的 ref 会直接抛异常崩溃)buzzerRingModeProvider(乐观 checkmark)+ 发一条 RI_CNT=N(BLE-or-cloud 路由)RI_CNT=? 读回验证(TAPD #1003273 撤掉的)——中继盒每收到一条 RI_CNT 就会响一声,选完再补一次查询会让盒子"di----di"响两声;设备真相改走连接时的 on-connect RI_CNT=? 查询 + onRingCountReported 回读桥接,本地乐观 pick 只负责撑住选完那一刻的 checkmarkboosterDisplayModeProvider + 发 TEMP_INT / TEMP_EXT / TEMP_ALL(BLE-or-cloud 路由,TAPD #1003254 起才能在 WiFi 模式 + 蓝牙关时也送达)TEMP=? 回读桥接;中继盒物理按键可改变显示模式,因此 boosterDisplayModeProvider 的本地持久化选择可能滞后于设备真实状态。因此当前实现没有 Display mode 的设备读回;App 只保存本地最后一次选择,物理按键改动不会同步到 App。boosterBacklightLevelProvider + 发 BL_LVL=N(TAPD #1003302),与 Display mode 同一套"本地乐观 pick,无读回"模型,同样可能滞后于物理按键改动Navigator.pushNamed(FirmwareUpgradePage.routeName, arguments: deviceId)forgetDevice(ref, deviceId)(服务端 unbind + 本地移除 + 停止云会话 + BLE 断开),然后尝试 Navigator.pop——多数情况下 ref.listen(connectedBoostersProvider) 会先一步自动 pop,这里的 pop 只是兜底watch 三个本地设置 Provider(buzzer / display / backlight)驱动各自行的副标题与 sheet 里的 ✔;ref.listen(connectedBoostersProvider) 驱动设备消失时的自动退出。WiFi 切换链路里的每一步轮询(_waitForBooster / _waitForCloudLive)都是 400ms 间隔的 while 循环读 Provider 当前值,不经 ref.listen。
无——Buzzer、Display mode、Backlight 等设置仍走既有的 BLE-or-cloud transport 抽象,iOS/Android 共用同一套逻辑;但 Connection 区的 Connection Mode 与 WiFi Network 两项在当前实现中属于 BLE-only,代码要求手机持有这台设备的原始 BLE/GATT 链路,云端-only 手机不能操作。
_isBleUp 之类的状态在中继盒掉线时可能读到 stale 值导致页面在已销毁的 ref 上崩溃(TAPD #1003274);中继盒自己上报的 WIFI_STS/MQTT_STS 状态字节实测约 10 秒才推一次,比 spec 写的 2 秒慢得多,直接等它会让"正在验证…"对话框多转 8 秒(TAPD #1003364);据此把验证顺序改成"App 自己先切云端 + 等自己会话的第一帧"优先、box 的 BLE 状态字节降级为兜底诊断(TAPD #1003370)。当前实现是这三轮修复之后的版本。boosterDisplayModeProvider 的本地持久化选择可能滞后于设备真实状态。Backlight 同样尚无 BL_LVL=? 读回桥接,本地 checkmark 只能反映 App 最后一次发送的值。RI_CNT 都响一声而导致的双响问题。