本页跟踪当前未关闭的事项:
图例:
补充:当前代码在后续启动(permIntroSeen == true)时,才会通过 AppPermissions.requestAllUpfront() 在 runApp() 前顺序触发系统权限请求;首次启动则会先显示 PermissionsIntroPage,再在用户继续后请求系统权限;该方法的注释明确说明它由 main 调用,并在 Flutter 首帧渲染前执行。只有用户点击 Continue 后,会在 PermissionsIntroPage 的 onContinue 回调中调用 _requestUpfrontAndMarkIntroSeen();但当前代码还有兜底路径:如果用户以其他方式离开 PermissionsIntroPage(例如返回键),_showPermissionIntro() 在路由返回后仍会调用 _requestUpfrontAndMarkIntroSeen(),因此并不只限于 Continue 按钮这一条路径。只有在后续启动里,才会恢复成 UI 挂载前的 upfront 请求。
permissions_service.dart:32-54):iOS 只请求 Permission.bluetooth + Permission.notification 两项
requestCM4LocalNetwork()(同文件)是 CM4 专用、按需触发 的 iOS Local Network 权限路径——通过 MethodChannel('tech.culina.meter/local_network') 发起 Bonjour browse 以弹出系统“本地网络”授权;不在 requestAllUpfront() 顺序里,与上述两项 runtime 权限分开处理。FlutterBluePlus.adapterState 主动实例化 CBCentralManager,以确保 CoreBluetooth 原生蓝牙授权弹窗真正触发;并额外记录 PERM_NATIVE_STATE,用于纠正 permission_handler 在 fresh install 时把 notDetermined 误映射成 permanently_denied 的日志假象。request()_userSetTargets / lastUserTargetWriteAt echo-suppression + ALARM_SYNC_ADOPT gate 已完整(详见 状态模型 §ProbeAlarmConfig "ALARM_SYNC_ADOPT gate" 段)SETT==<32hex> push 已不是最小 handler,反向同步覆盖面已大幅扩展,但 BLE 与 MQTT 两条路径并不完全同源同逻辑——两者都会在收到 SETT== 后触发 onProbeAlarmConfigUpdated(报警配置本身)、onProbeTargetReconciled(目标温度)、onProbeMeatTypeReconciled(肉种)三个回调(ble_device_service.dart:3709,3840,3932、mqtt_service.dart:834,902,931,1037,1040);但熟度(doneness)没有对等实现——onProbeDonenessReconciled 只在 mqtt_service.dart(云端路径,:960,1044)里存在并被调用,ble_device_service.dart 通篇不含这个回调(settings.donenessWireF 在 BLE 路径里只进了诊断日志字符串,从未喂给任何 reconcile 回调)。也就是说:用户在 CM4 物理屏上改熟度时,只有走云端/MQTT 的手机会反向同步,纯 BLE 连接(未配网或 BLE-only 强制模式)的手机不会——这是一个真实、狭窄的残留缺口,不是"已完全对等"。738fdeb / TAPD #1003233(Beta 2026-06-11)已定论——Mute 纯走 booster-level RI_CNT=4,固件只接受探针配置 A∈{0,1},之前设想的 per-cook A=2 静音镜像已被否决并从代码里移除。ble_device_service.dart:3709,3840,3932、mqtt_service.dart:834,902,931,960,1037,1040,1044;738fdeb(TAPD #1003233)/sett MQTT topic 未 subscribe/CM4/<id>/data 与 /CM4/<id>/sett,并把下行命令 publish 到 /CM4/<id>/apps;/sett 现在用于接收 SETT==、WIFI_STS、MQTT_STS 等中继盒推送——MQTT 路径把 SETT== 解析为 CM4SettingsAscii 后交给 _handleSettingsAscii,与 BLE 路径同源逻辑。"只在 BLE 通道下被接到、MQTT 通道下被丢"的旧描述已不符合当前代码。/CM4/<id>/apps,但代码也会把可镜像设置写(如 SET_<color>=...、SET_C、SET_F)额外 publish 到 /CM4/<id>/sett,接收侧通过 _tryAdoptPeerSettingMirror(...) 做 echo-suppression / peer adopt;因此 /sett 仍承载一类 App→App 的镜像写入,不是纯粹单向——这是现状说明,不是待办。补充:当前 MQTT 路径在会话首个 cloud frame 到达后,还会主动通过云端在 /apps 发送一次 SET_RD,再用 /sett 返回的 SETT== 拉取并采纳各探针当前配置。因此即使手机处于 cloud-only 会话(BT 关闭、未持有 GATT),也会在首帧后补齐 target / alarm state,而不只是被动等待后续物理按键推送。
onTaskRemoved 钩子(TAPD #9)补充:当前 Dart 侧统一退出路径 _AppExit.run() 已不只清理 BLE / MQTT。它还会先调用 finalizeActiveForAppExit() 收尾活跃烹饪会话,并在最后结束 iOS Live Activities;因此后续如果把原生 onTaskRemoved 接到这条路径,实际覆盖的是整套退出清理,而不是仅断开连接。
BleDeviceService.shutdown()(TAPD #9 注释:幂等、可 await,会取消重连/宽限期/关机计时器等并下钻到 BleService.shutdown() 做 GATT 拆除);onTaskRemoved 接入后应调用与 _AppExit.run() 相同的这条 shutdown 链,而不是仅断开单连接。MainActivity.kt 只重写了 onDestroy,未重写 onTaskRemoved
onTaskRemoved 是 Service 子类的 callback,不是 Activity callback;当前代码库已存在原生 BleMonitoringService 前台服务,后续应把该钩子接到现有 Service,而不是再“新建一个 Service”main.dart:205-211)已预留 'onTaskRemoved' 分支onDestroy 来不及调,BLE / MQTT 未清理BleMonitoringService.kt 已经是一个 Service 子类(承载前台服务通知),后续 onTaskRemoved 应放到它身上8829902,notification_service.dart:103 / :280-281):
DarwinInitializationSettings(requestCriticalPermission: true)ambientOverTemp / internalOverTemp 用 InterruptionLevel.critical,其它 11 类保留 timeSensitivegranted=false——Apple 必须先颁发 com.apple.developer.usernotifications.critical-alerts entitlementhttps://developer.apple.com/contact/request/notifications-critical-alerts(justification:厨房温度计安全报警 >100°C 内温 / >275°C 外温必须 bypass Silent + DND)ios/Runner/Runner.entitlements → Xcode 重生 provisioning profile → 重建 → 真机验证(Silent + DND 状态下触发 safety alarm,铃声应 override)BleDeviceService._ensurePermissions 在每次 BLE 操作前 lazy 检查 + 必要时重新请求(permissions_service.dart:9)补充:当前 CM4 配网还带有一套明确的重连与判定常量。发送 PSWD= 前就会把 bleService.suppressReconnect 置为 true,然后再发送 PSWD=;之后等待 4 秒再手动重锁 BLE。_reconnectBleToBooster() 最多尝试 5 次、每次失败后等待 3 秒。随后最终配对 verdict 会在 90 秒窗口内接受两类成功信号之一:latched 的 MQTT_STS=1,或该设备的第一帧 cloud telemetry;两者都没到才判失败。
补充:当前 CM4 配网流程在发送 SSID= / PSWD= 之前,还会先通过 BLE 向目标设备写入 M_ID=<deviceId> 与 M_PD=<password>,作为中继盒自身的 MQTT 登录凭据。并且在配网校验阶段,页面会启动一个 5 秒一次的 BLE keep-alive / re-lock 定时器:链路正常时重发 CNT_0,链路掉线时主动重连,直到配网 verdict 结束,以免错过仅通过 BLE 推送的 WIFI_STS / MQTT_STS。另外,当前实现还会在这两步之后、SSID= 之前,再通过 BLE 发送 HOST=<brokerUrl>,把中继盒的 MQTT broker 固定到账号当前 server region;这一步失败只记日志,不会中断后续配网。
093cef5(TAPD #1003192,"route CM4 SSID=/PSWD= to the target booster, not first-connected")——wifi_setup_page.dart 已通过 sendCommandAsyncTo(widget.deviceId, ...) 显式按 deviceId 路由 SSID/PSWD 写入,修复了旧的 first-connected-fallback 误写问题c613148):60s 是中继盒固件层独占锁 TTL(与 legacy 0x55B1 同机制),不是 BLE 监督超时;App 每 45s 发 CNT_0 刷新锁(c613148 之前是 20s),锁到期后中继盒主动 shed GATT 链路。CM4 自己是否会另外主动断开纯 idle 连接——这条细节仍 unknown,但已不影响任何当前设计决策。详见 CM4 协议 §Q4PENON 1..4 还是颜色名 PENON BLACK/WHITE/BLUE/RED?是否有版本依赖?🔴cm4_protocol.dart 列的是 App 当前发的命令,不是 CM4 固件支持的全集——timezone、language、factory reset、explicit alarm-state push 等待问 🔴/CM4/<id>/data(遥测)、/CM4/<id>/sett(中继盒推送 + App 镜像写入)、/CM4/<id>/apps(028087a / V14 起新增,App 主下行命令通道)。是否还有 App 未订阅/发布的其他 topic(status / OTA-progress / 离线遗嘱等)仍 unknown——这条问题本身不因发现 /apps 而完全关闭 🔴tempIntArray 为什么是 121 条 🔴 ——代码仅说明范围 0–120 °C(121 个整点值),没有解释为什么是这个范围而不是其他。怀疑点:与中继盒固件的 NTC 查表对齐,或是探针物理量程。附注:tempExtArray 是 275 条(0–274 °C),与 275 °C 的"全屏报警钳位"一致以下三条此前只写在各自的技术细节页里,未出现在本清单——但都是明确写着"pending Liang" / "待 Liang 确认固件"的真实开放项,收录于此以便 Beta/Liang 从统一入口就能看到:
SET_<color>=000000 报警 disarm 的中继盒侧效果未经硬件验证 🔴 ——App 在 CM4 Confirm 路径上把 SET_<color>=000000(字段 A=0)当作物理按键 disarm 的 wire 等价用,但尚未确认固件在正在 firing 的报警状态下是否真的会清掉 icon + backlight——若固件在 alarming 状态下忽略 A=0,需要固件侧改为让 RING_OFF 在 mute 模式下连同视觉一起清。详见 CM4 协议 与 告警与通知 的 3c40aa9 / TAPD #1003214 段。/CM4/<id>/data 时让 App 走 BLE 复测确认。详见 MQTT 与云端 "Liang 待办" 段。补充:当前 Settings 已有 Server Region 行。普通用户看到的是只读的当前区域文本;只有在开发者模式开启时,才会显示可点击的选择器并允许手动切换区域。
另外,当前已登录账号下的区域切换不是单纯本地 UI 选项:代码会先弹确认框,确认后调用 accountRegionServiceProvider.resetRegionData(...) 清空旧 server region 的账号数据,再执行 applyLockedRegion(...) 应用新区域。切换后,App 只会对当前 BLE 在线的 CM4 发送 HOST= 并继续绑定;其余不在 BLE 链路上的 CM4 会被视为 stranded,并通过 forgetDevice(...) 从本地状态中移除,避免一个账号同时分裂在两个 region。只有未登录状态下,才是单纯执行本地 regionSvc.setRegion(...) 切换。
connection_mode_page.dart 的 WiFi 选项)settings_page.dart 源码未显示 Settings 页手动进入 WiFi 配置页的入口cookingSessionsProvider 的活跃会话当前 UI 不再是本地 switch;点击 Notifications 行会直接跳转系统通知设置(Android 优先打开应用通知页,失败时回退到 App Settings;iOS 打开 App Settings)。
不存在本地 _notificationsEnabled 这个标识符;当前代码使用 notificationsEnabledProvider,其构造函数先乐观默认 true,随后会在启动时和每次返回前台时读取系统 Permission.notification.status 覆盖状态,通知开关的实际控制权已交给系统通知设置。
✅ 当前代码已接通系统通知设置入口,不属于待确认事项
当前 Support 页没有本地假发送按钮;Email 会调用系统邮件应用并预填诊断信息,Instagram 与 Website 也会通过系统外部应用打开。
当前 Support 页不会伪造“已发送”成功提示;Email 行会构造 mailto: 并交给系统邮件应用,失败时仅复制邮箱地址并提示用户,Instagram/Website 也都直接交给系统外部应用打开。
当前代码已通过 SupportDiagnostics.buildMailtoUri(...) 构造 mailto: 并交给系统邮件应用打开;若系统无邮件应用,则仅复制邮箱地址并提示用户。此项不应再描述为“需要接 API 或改为 mailto: 自动填充”。
c072acf) 接通调用点(cooking_page._pushTargetToDevice CM4 分支改调 Cm4BoosterTransport.setProbeTarget)a3c9cf7 + Beta 2026-05-11 spec 收口编码——CD 槽是大写十六进制 00–FF(鸡 165°F → A5),守卫从 > 99 放宽到 > 0xFF6e5dfb7(2026-05-19)反向恢复 SET_BL/WH/BU/YE 色名前缀(cloud doc 2026-05-19 权威 + Liang 2026-05-16 HW 实测 SET_01..04 无应答)_reconnectDelayUltraFast / _reconnectUltraFastAttempts 两个常量随删),代码对齐 v4.2 §六的 15s / 60s / 5min 分段ble_device_service.dart:368-414BleDeviceService._bgTieredCadenceArmTimer(一次性 Timer(4h, _enterBgTieredCadence),ble_device_service.dart:484)+ _inBgTieredCadenceMode 标志(:491)实现lib/core/transport/ 现是 main 的一部分ProbeNumber.colorName (Black/White/Blue/Yellow) + labelFor(deviceId) → CM2_F07F_Black(下划线分隔,QA 2026-05-18 修正)AlarmThresholds.{internalHiClampC=101, internalHardClampC=120, internalMaxDisplayF=248}USUS=N / USUS=? 区域索引命令;CM4 现改为用 HOST= 写入完整 broker URL、用 HOST=? 读回当前 host。0x55B2(覆盖 Beta 先前的 0x55B1 回复)