diff --git a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/KeyBridge.kt b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/KeyBridge.kt index 108ee5c..2113885 100644 --- a/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/KeyBridge.kt +++ b/Fly App/android/app/src/main/kotlin/com/pilotvault/flyapp/KeyBridge.kt @@ -113,7 +113,11 @@ class KeyBridge(private val ctx: BridgeCtx) : SubBridge { ctx.emit(mapOf("type" to "key", "id" to id, "value" to KeyValues.toDart(value))) } - override fun onFailure(error: DJIError) = Unit + // Supported but unreadable (the Mavic Pro remote's gimbal dial + // speed): say so, or the row waits on "—" forever. + override fun onFailure(error: DJIError) { + ctx.emit(mapOf("type" to "key", "id" to id, "unreadable" to true)) + } }) result.success(id) } diff --git a/Fly App/lib/fly/state/key_state.dart b/Fly App/lib/fly/state/key_state.dart index b50cee8..d386fed 100644 --- a/Fly App/lib/fly/state/key_state.dart +++ b/Fly App/lib/fly/state/key_state.dart @@ -25,8 +25,17 @@ class KeyState extends ChangeNotifier with SettingsOverlay { notifyListeners(); } + /// A key the aircraft calls supported but will not read: hidden like an + /// unsupported one, until a value turns up after all. + void markUnreadable(String id) { + if (_supported[id] == false || reportedSettings.containsKey(id)) return; + _supported[id] = false; + notifyListeners(); + } + /// One `key` event: `{id, value}`. void apply(String id, Object? value) { + if (_supported[id] == false) _supported.remove(id); replaceReported({...reportedSettings, id: value}); notifyListeners(); } diff --git a/Fly App/lib/fly/telemetry_reducer.dart b/Fly App/lib/fly/telemetry_reducer.dart index 5b99bbb..ea90f83 100644 --- a/Fly App/lib/fly/telemetry_reducer.dart +++ b/Fly App/lib/fly/telemetry_reducer.dart @@ -49,7 +49,12 @@ class TelemetryReducer { return true; case 'key': final String? id = asString(event['id']); - if (model.connected && id != null) model.keys.apply(id, event['value']); + if (!model.connected || id == null) return true; + if (event['unreadable'] == true) { + model.keys.markUnreadable(id); + } else { + model.keys.apply(id, event['value']); + } return true; case 'mission': model.mission.apply(event); diff --git a/Fly App/test/fly/settings_test.dart b/Fly App/test/fly/settings_test.dart index ce84395..1d35812 100644 --- a/Fly App/test/fly/settings_test.dart +++ b/Fly App/test/fly/settings_test.dart @@ -146,6 +146,17 @@ void main() { expect(m.keys.value(SettingsKeys.novice), isNull); }); + test('a supported key that cannot be read hides its row until a value arrives', () { + final FlightModel m = connectedModel(phantom4); + final TelemetryReducer r = TelemetryReducer(m); + m.keys.setSupported(SettingsKeys.gimbalDialSpeed, true); + r.apply({'type': 'key', 'id': SettingsKeys.gimbalDialSpeed.id, 'unreadable': true}); + expect(m.keys.supported(SettingsKeys.gimbalDialSpeed), isFalse); + r.apply({'type': 'key', 'id': SettingsKeys.gimbalDialSpeed.id, 'value': 40}); + expect(m.keys.supported(SettingsKeys.gimbalDialSpeed), isNot(isFalse)); + expect(m.keys.value(SettingsKeys.gimbalDialSpeed), 40); + }); + test('subscriptions listen only to supported keys and cancel on dispose', () async { final FakeDjiService dji = FakeDjiService()..unsupportedKeys.add('ESC_BEEPING_ENABLED'); final KeyState s = KeyState();