Skip to content

Detect unsupported Bee firmware and warn user#5744

Merged
mdmohsin7 merged 5 commits intomainfrom
feat/detect-unsupported-bee-firmware
Mar 17, 2026
Merged

Detect unsupported Bee firmware and warn user#5744
mdmohsin7 merged 5 commits intomainfrom
feat/detect-unsupported-bee-firmware

Conversation

@mdmohsin7
Copy link
Copy Markdown
Member

@mdmohsin7 mdmohsin7 commented Mar 17, 2026

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
  • Connect to a Bee device with firmware >= 0.6.1 — should show "Firmware Not Supported" warning explaining encrypted audio incompatibility
  • 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

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented Mar 17, 2026

Greptile Summary

This PR queries the real firmware version from Bee devices via BLE command 0xC005, replaces the previous hardcoded '1.0.0' revision, and gates on firmware >= 0.6.1 to show a blocking "Firmware Not Supported" warning (bypassing the "don't show again" preference). The connection-time firmware query is then surfaced through BtDevice.isBeeFirmwareUnsupported and used to control both the warning title and message copy.

Key concerns:

  • Undocumented byte order in firmware parsing (bee_connection.dart:107–110): The response bytes are read as [patch, minor, major] with no comment explaining the Bee protocol's encoding. If the actual wire format is [major, minor, patch], all firmware versions would be mis-parsed, potentially triggering the warning for every user.
  • '0.6.0' timeout fallback can hide unsupported firmware (bee_connection.dart:144): When the 5-second BLE query times out, _cachedFirmwareVersion stays null and getDeviceInfo() returns '0.6.0' (compatible), silently suppressing the warning even if the device is running incompatible firmware. An empty-string fallback would leverage the existing if (fw.isEmpty) return false guard and be a safer no-op.
  • isCritical duplicates model-layer logic in the UI (found_devices.dart:167): The critical-firmware check is re-expressed inline rather than delegated to a model method, creating a maintenance burden if other device types gain critical warnings in the future.

Confidence Score: 2/5

  • Not safe to merge — the undocumented byte-order assumption and the timeout-fallback silent-pass could cause the unsupported-firmware warning to either fire for everyone or never fire, depending on the actual Bee protocol and BLE conditions.
  • Two P1 logic issues: (1) the reversed byte order [patch, minor, major] is undocumented and if wrong would systematically mis-classify all devices; (2) the '0.6.0' fallback on timeout means a device genuinely running >= 0.6.1 that doesn't respond within 5 seconds will never show the warning. Both need verification or a code fix before this is safe for production.
  • app/lib/services/devices/bee_connection.dart — byte-order parsing and timeout fallback value

Important Files Changed

Filename Overview
app/lib/services/devices/bee_connection.dart Adds firmware query via BLE command 0xC005 and caches the result; introduces an undocumented reversed byte order assumption and a '0.6.0' fallback that could silently suppress the unsupported-firmware warning on timeout. Also creates a duplicate subscription on the control characteristic (pre-existing concern from review thread).
app/lib/backend/schema/bt_device/bt_device.dart Adds isBeeFirmwareUnsupported getter and conditional firmware warning title/message for Bee devices; logic is correct for version comparison. Hardcoded user-facing strings (pre-existing l10n concern from review thread).
app/lib/pages/onboarding/find_device/found_devices.dart Correctly switches firmware warning to use connectedDevice (with real firmware info) instead of the stale scan result, and adds critical-warning bypass for acknowledged preferences. isCritical check duplicates model logic.

Sequence Diagram

sequenceDiagram
    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
Loading

Last reviewed commit: 3497f1d

return {
'modelNumber': 'Bee',
'firmwareRevision': '1.0.0',
'firmwareRevision': _cachedFirmwareVersion ?? '1.0.0',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P0 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 _getDeviceInfoFromBeecopyWith(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':

Suggested change
'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.

Comment on lines +117 to +139
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();
}
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.

Comment on lines 665 to 673
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.';
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 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.
@mdmohsin7
Copy link
Copy Markdown
Member Author

@greptile-apps re-review

return {
'modelNumber': 'Bee',
'firmwareRevision': '1.0.0',
'firmwareRevision': _cachedFirmwareVersion ?? '0.6.0',
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 '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.

@mdmohsin7 mdmohsin7 merged commit e84db0e into main Mar 17, 2026
2 checks passed
@mdmohsin7 mdmohsin7 deleted the feat/detect-unsupported-bee-firmware branch March 17, 2026 11:06
Glucksberg pushed a commit to Glucksberg/omi-local that referenced this pull request Apr 28, 2026
## 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)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant