Detect unsupported Bee firmware and warn user#5744
Conversation
Greptile SummaryThis PR queries the real firmware version from Bee devices via BLE command Key concerns:
Confidence Score: 2/5
Important Files Changed
Sequence DiagramsequenceDiagram
participant UI as FoundDevices (UI)
participant Provider as OnboardingProvider
participant Conn as BeeDeviceConnection
participant BLE as BLE Transport
participant Device as BtDevice (model)
UI->>Provider: handleTap(device)
Provider->>Conn: connect(onConnectionStateChanged)
Conn->>Conn: super.connect() — sets up _controlSub
Conn->>BLE: subscribe to control characteristic (_controlSub)
Conn->>Conn: _queryFirmwareVersion()
Conn->>BLE: subscribe to control characteristic (temp sub)
Conn->>BLE: writeCharacteristic(CMD_GET_FIRMWARE_VERSION 0xC005)
BLE-->>Conn: firmware response [patch, minor, major]
Conn->>Conn: parse → _cachedFirmwareVersion = "major.minor.patch"
Conn->>Conn: device.copyWith(firmwareRevision: _cachedFirmwareVersion)
Conn->>BLE: cancel temp subscription
Provider-->>UI: isConnected = true
UI->>Provider: connectedDevice (has real firmware)
UI->>Device: getFirmwareWarningTitle() / getFirmwareWarningMessage()
Device->>Device: isBeeFirmwareUnsupported (fw >= 0.6.1?)
alt firmware unsupported
UI->>UI: show "Firmware Not Supported" dialog (no dismiss pref)
else firmware OK or empty
UI->>UI: show "Compatibility Note" dialog (with dismiss pref)
end
Last reviewed commit: 3497f1d |
| return { | ||
| 'modelNumber': 'Bee', | ||
| 'firmwareRevision': '1.0.0', | ||
| 'firmwareRevision': _cachedFirmwareVersion ?? '1.0.0', |
There was a problem hiding this comment.
Fallback
'1.0.0' incorrectly triggers unsupported firmware warning
When _queryFirmwareVersion() times out or fails, _cachedFirmwareVersion remains null, so getDeviceInfo() returns the hardcoded fallback '1.0.0'. This value then flows through _getDeviceInfoFromBee → copyWith(firmwareRevision: '1.0.0'), setting the device's firmware revision to '1.0.0'.
isBeeFirmwareUnsupported then evaluates 1.0.0: major=1 > 0 → true, incorrectly classifying a failed query as "Firmware Not Supported". Every Bee user with a device that doesn't respond in time (or whose firmware predates this BLE command) will see the critical unsupported warning.
The fallback should be a value that either (a) stays empty/unknown so the if (fw.isEmpty) return false guard fires, or (b) reflects an explicitly compatible version such as '0.1.0':
| 'firmwareRevision': _cachedFirmwareVersion ?? '1.0.0', | |
| 'firmwareRevision': _cachedFirmwareVersion ?? '', |
With an empty fallback, isBeeFirmwareUnsupported returns false early at the if (fw.isEmpty) return false check, avoiding the false positive.
| Future<List<int>?> sendFirmwareCommand() async { | ||
| final completer = Completer<List<int>>(); | ||
| late final StreamSubscription sub; | ||
|
|
||
| sub = transport.getCharacteristicStream(serviceUuid, controlCharacteristicUuid).listen((data) { | ||
| final response = parseResponse(data); | ||
| if (response['type'] == 'response' && response['code'] == beeCmdGetFirmwareVersion) { | ||
| if (!completer.isCompleted) { | ||
| completer.complete(response['payload'] as List<int>); | ||
| } | ||
| } | ||
| }); | ||
|
|
||
| try { | ||
| final command = encodeCommand(beeCmdGetFirmwareVersion, []); | ||
| await transport.writeCharacteristic(serviceUuid, controlCharacteristicUuid, command); | ||
| return await completer.future.timeout(const Duration(seconds: 5)); | ||
| } on TimeoutException { | ||
| return null; | ||
| } finally { | ||
| await sub.cancel(); | ||
| } | ||
| } |
There was a problem hiding this comment.
Duplicate BLE subscription during firmware query
BeeDeviceConnection.connect() calls super.connect() first, which sets up _controlSub on the same controlCharacteristicUuid stream. Then sendFirmwareCommand() creates a second concurrent subscription to the same characteristic. Because the stream is broadcast both listeners receive every BLE packet, creating unnecessary processing overhead during the 5-second window.
Consider reusing the existing _sendCommand infrastructure from CustomDeviceConnection (which already registers a completer in _responseControllers) instead of opening a standalone subscription:
Future<List<int>?> sendFirmwareCommand() async {
return await _sendCommand(beeCmdGetFirmwareVersion, []);
}This eliminates the duplicate subscription and keeps response routing in one place.
| case DeviceType.bee: | ||
| if (isBeeFirmwareUnsupported) { | ||
| return 'Your $name is running firmware v$firmwareRevision which uses encrypted audio that Omi cannot process.\n\n' | ||
| 'Please downgrade your Bee firmware to a version below 0.6.1 for compatibility with Omi.\n\n' | ||
| 'Audio capture will not work with the current firmware.'; | ||
| } | ||
| return 'Your $name\'s current firmware works great with Omi.\n\n' | ||
| 'We recommend keeping your current firmware and not updating through the Bee app, as newer versions may affect compatibility.\n\n' | ||
| 'For the best experience, please keep your current firmware version.'; |
There was a problem hiding this comment.
User-facing strings are hardcoded instead of using l10n
Per the Flutter localization rule, all user-facing strings must use the l10n system. The new warning title 'Firmware Not Supported' and body message (lines 667–669) are hardcoded string literals. The same issue applies to the updated "Compatibility Note" message body on lines 671–673 and to getFirmwareWarningTitle() at line 648.
These strings should be added to the app's ARB localization files and referenced via context.l10n.* (or equivalent accessor) rather than being inlined in the model layer.
Rule Used: Flutter localization - all user-facing strings mus... (source)
Change fallback from '1.0.0' (major=1 triggers unsupported check) to '0.6.0' (last known compatible version) when BLE firmware query fails.
Unsupported firmware warnings bypass the acknowledgment cache so users who previously dismissed the milder Compatibility Note still see the critical warning. Also hide the "Don't Show Again" checkbox for critical warnings.
|
@greptile-apps re-review |
| return { | ||
| 'modelNumber': 'Bee', | ||
| 'firmwareRevision': '1.0.0', | ||
| 'firmwareRevision': _cachedFirmwareVersion ?? '0.6.0', |
There was a problem hiding this comment.
'0.6.0' fallback silently suppresses unsupported-firmware warning on timeout
When _queryFirmwareVersion() times out (e.g. BLE congestion, or rare device unresponsiveness), _cachedFirmwareVersion is null and getDeviceInfo() returns the hardcoded '0.6.0'. Since 0.6.0 < 0.6.1, isBeeFirmwareUnsupported evaluates to false and the warning is never shown — even though the device may be running incompatible firmware.
While the argument is that newer (unsupported) firmware reliably implements CMD_GET_FIRMWARE_VERSION, that assumption can break under real-world BLE conditions. A user with an unsupported device could silently experience audio capture failures without any indication that firmware is the cause.
Consider returning an empty string on timeout so the if (fw.isEmpty) return false guard already in isBeeFirmwareUnsupported gracefully no-ops, while still keeping a separate path for genuinely queried versions:
'firmwareRevision': _cachedFirmwareVersion ?? '',This leaves the unsupported-firmware detection logic unchanged for successfully-queried devices, and avoids showing a false "compatible" version when the query actually failed.
## Summary - Query real firmware version from Bee device via BLE command `CMD_GET_FIRMWARE_VERSION` (0xC005) instead of hardcoding `1.0.0` - Detect Bee firmware >= 0.6.1 which uses AES-128-CTR encrypted audio that Omi cannot decode - Show "Firmware Not Supported" warning with actionable message when unsupported firmware is detected - Fix firmware warning to use the connected device (with real firmware info) instead of the stale scan result ## Test plan - [ ] Connect to a Bee device with firmware < 0.6.1 — should show the existing "Compatibility Note" warning - [x] Connect to a Bee device with firmware >= 0.6.1 — should show "Firmware Not Supported" warning explaining encrypted audio incompatibility - [x] Verify firmware version is correctly parsed from BLE response (major.minor.patch format) - [ ] Verify timeout handling if device doesn't respond to firmware command 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Summary
CMD_GET_FIRMWARE_VERSION(0xC005) instead of hardcoding1.0.0Test plan
🤖 Generated with Claude Code