fix(#697): stop swallowing an unconfirmed BLE disconnect; retry + warn

disconnect() now caps the platform-confirm wait at 10 s (the vendored
Android plugin's backstop resolves within ~5 s), retries once after
500 ms, and when both attempts fail sets bleReleaseUnconfirmed instead
of pretending the release worked. The scanner screen shows a
persistent, dismissible banner naming the recovery (toggle Bluetooth /
force-stop), per the error-visibility standard. Cleared on the next
connect or disconnect attempt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull/701/head
Strycher 5 days ago
parent 61903f526b
commit 07bef59eb6

@ -255,6 +255,18 @@ class MeshCoreConnector extends ChangeNotifier {
String? _lastDeviceId;
String? _lastDeviceDisplayName;
bool _manualDisconnect = false;
// Set when a manual BLE disconnect was never confirmed by the platform, so
// the OS may still hold the radio and it stops advertising (#689/#697).
// Cleared on the next connect, the next disconnect attempt, or UI dismiss.
bool _bleReleaseUnconfirmed = false;
bool get bleReleaseUnconfirmed => _bleReleaseUnconfirmed;
void clearBleReleaseWarning() {
if (!_bleReleaseUnconfirmed) return;
_bleReleaseUnconfirmed = false;
notifyListeners();
}
final MeshCoreUsbManager _usbManager = MeshCoreUsbManager();
final LinuxBlePairingService _linuxBlePairingService =
LinuxBlePairingService();
@ -2364,6 +2376,7 @@ class MeshCoreConnector extends ChangeNotifier {
_lastDeviceId = _deviceId;
_lastDeviceDisplayName = _deviceDisplayName;
_manualDisconnect = false;
_bleReleaseUnconfirmed = false;
_cancelReconnectTimer();
_bleInitialSyncStarted = false;
if (PlatformInfo.isWeb) {
@ -3130,11 +3143,32 @@ class MeshCoreConnector extends ChangeNotifier {
await _translationService?.releaseModel();
if (!skipBleDeviceDisconnect) {
try {
// Skip queued BLE operations so disconnect doesn't get stuck behind them.
await _device?.disconnect(queue: false);
} catch (e) {
_appDebugLogService?.warn('Disconnect error: $e', tag: 'BLE Connect');
_bleReleaseUnconfirmed = false;
var released = _device == null;
for (var attempt = 1; attempt <= 2 && !released; attempt++) {
try {
// Skip queued BLE operations so disconnect doesn't get stuck behind
// them. Cap the platform-confirm wait well below FBP's 35 s default;
// the vendored Android plugin force-closes and confirms within ~5 s
// when the OS never reports the disconnect (#696).
await _device?.disconnect(queue: false, timeout: 10);
released = true;
} catch (e) {
_appDebugLogService?.warn(
'Disconnect attempt $attempt failed: $e',
tag: 'BLE Connect',
);
if (attempt == 1) {
await Future<void>.delayed(const Duration(milliseconds: 500));
}
}
}
if (!released &&
transportAtDisconnect == MeshCoreTransportType.bluetooth) {
// Both attempts failed: the OS never confirmed the release, so this
// device may still hold the radio and keep it from advertising
// (#689). Surfaced as a persistent warning, never swallowed.
_bleReleaseUnconfirmed = true;
}
} else {
_appDebugLogService?.info(

@ -134,6 +134,14 @@
"scanner_scan": "Scan",
"scanner_bluetoothOff": "Bluetooth is off",
"scanner_bluetoothOffMessage": "Please turn on Bluetooth to scan for devices",
"scanner_bleReleaseWarning": "Radio may still be held by this device",
"@scanner_bleReleaseWarning": {
"description": "Persistent banner title when a BLE disconnect was never confirmed by the OS, so the radio may stay latched and stop advertising (#697)."
},
"scanner_bleReleaseWarningMessage": "The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.",
"@scanner_bleReleaseWarningMessage": {
"description": "Persistent banner body naming the recovery for an unconfirmed BLE release (#697)."
},
"scanner_chromeRequired": "Chrome Browser Required",
"scanner_chromeRequiredMessage": "This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.",
"scanner_enableBluetooth": "Enable Bluetooth",

@ -640,6 +640,18 @@ abstract class AppLocalizations {
/// **'Please turn on Bluetooth to scan for devices'**
String get scanner_bluetoothOffMessage;
/// Persistent banner title when a BLE disconnect was never confirmed by the OS, so the radio may stay latched and stop advertising (#697).
///
/// In en, this message translates to:
/// **'Radio may still be held by this device'**
String get scanner_bleReleaseWarning;
/// Persistent banner body naming the recovery for an unconfirmed BLE release (#697).
///
/// In en, this message translates to:
/// **'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.'**
String get scanner_bleReleaseWarningMessage;
/// No description provided for @scanner_chromeRequired.
///
/// In en, this message translates to:

@ -290,6 +290,14 @@ class AppLocalizationsBg extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Моля, активирайте Bluetooth, за да сканирате за устройства.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Изисква се браузър Chrome';

@ -293,6 +293,14 @@ class AppLocalizationsDe extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Bitte aktivieren Sie Bluetooth, um nach Geräten zu suchen.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome Browser erforderlich';

@ -287,6 +287,14 @@ class AppLocalizationsEn extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Please turn on Bluetooth to scan for devices';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome Browser Required';

@ -291,6 +291,14 @@ class AppLocalizationsEs extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Por favor, active el Bluetooth para escanear dispositivos.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Navegador Chrome requerido';

@ -293,6 +293,14 @@ class AppLocalizationsFr extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Veuillez activer le Bluetooth pour rechercher des appareils.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Navigateur Chrome requis';

@ -290,6 +290,14 @@ class AppLocalizationsHu extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Kérjük, kapcsolja be a Bluetooth-ot, hogy eszközök keresése lehessen.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome böngésző szükséges';

@ -293,6 +293,14 @@ class AppLocalizationsIt extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Si prega di attivare il Bluetooth per effettuare la scansione dei dispositivi.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Browser Chrome richiesto';

@ -280,6 +280,14 @@ class AppLocalizationsJa extends AppLocalizations {
@override
String get scanner_bluetoothOffMessage => 'Bluetoothを有効にして、デバイスを検索してください。';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome ブラウザが必須です';

@ -279,6 +279,14 @@ class AppLocalizationsKo extends AppLocalizations {
@override
String get scanner_bluetoothOffMessage => '블루투스를 켜서 장치를 검색해주세요.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => '크롬 브라우저 필요';

@ -289,6 +289,14 @@ class AppLocalizationsNl extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Zorg ervoor dat Bluetooth is ingeschakeld om naar apparaten te zoeken.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome-browser vereist';

@ -294,6 +294,14 @@ class AppLocalizationsPl extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Prosimy włączyć Bluetooth, aby przeskanować urządzenia.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Wymagana przeglądarka Chrome';

@ -292,6 +292,14 @@ class AppLocalizationsPt extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Por favor, ative o Bluetooth para escanear por dispositivos.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Navegador Chrome necessário';

@ -292,6 +292,14 @@ class AppLocalizationsRu extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Пожалуйста, включите Bluetooth, чтобы найти устройства.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Требуется браузер Chrome';

@ -291,6 +291,14 @@ class AppLocalizationsSk extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Prosím, zapnite Bluetooth, aby ste mohli skenovať pre zariadenia.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Vyžaduje sa prehliadač Chrome';

@ -289,6 +289,14 @@ class AppLocalizationsSl extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Prosimo, vklopite Bluetooth, da lahko poiščete naprave.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Zahtevan brskalnik Chrome';

@ -288,6 +288,14 @@ class AppLocalizationsSv extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Vänligen aktivera Bluetooth för att söka efter enheter.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Chrome-webbläsare krävs';

@ -291,6 +291,14 @@ class AppLocalizationsUk extends AppLocalizations {
String get scanner_bluetoothOffMessage =>
'Будь ласка, увімкніть Bluetooth, щоб сканувати пристрої.';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => 'Потрібен браузер Chrome';

@ -277,6 +277,14 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get scanner_bluetoothOffMessage => '请开启蓝牙以搜索设备';
@override
String get scanner_bleReleaseWarning =>
'Radio may still be held by this device';
@override
String get scanner_bleReleaseWarningMessage =>
'The disconnect was not confirmed. If the radio does not reappear, toggle Bluetooth off and on, or force-stop the app.';
@override
String get scanner_chromeRequired => '需要 Chrome 浏览器';

@ -133,6 +133,13 @@ class _ScannerScreenState extends State<ScannerScreen> {
if (_bluetoothState == BluetoothAdapterState.off)
_bluetoothOffWarning(context),
// Unconfirmed BLE release: the OS never confirmed the last
// disconnect, so this device may still hold the radio and
// keep it from advertising (#689). Persistent until
// dismissed, per the error-visibility standard.
if (connector.bleReleaseUnconfirmed)
_bleReleaseWarning(context, connector),
// Status bar
_buildStatusBar(context, connector),
@ -435,6 +442,48 @@ class _ScannerScreenState extends State<ScannerScreen> {
return pin;
}
Widget _bleReleaseWarning(BuildContext context, MeshCoreConnector connector) {
final errorColor = Theme.of(context).colorScheme.error;
return Container(
width: double.infinity,
padding: const EdgeInsets.symmetric(vertical: 12, horizontal: 16),
color: errorColor.withValues(alpha: 0.15),
child: Row(
children: [
Icon(Icons.bluetooth_connected, size: 24, color: errorColor),
const SizedBox(width: 12),
Expanded(
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
context.l10n.scanner_bleReleaseWarning,
style: TextStyle(
color: errorColor,
fontWeight: FontWeight.w600,
fontSize: 14,
),
),
const SizedBox(height: 4),
Text(
context.l10n.scanner_bleReleaseWarningMessage,
style: TextStyle(
color: errorColor.withValues(alpha: 0.85),
fontSize: 12,
),
),
],
),
),
TextButton(
onPressed: connector.clearBleReleaseWarning,
child: Text(context.l10n.common_ok),
),
],
),
);
}
Widget _bluetoothOffWarning(BuildContext context) {
final errorColor = Theme.of(context).colorScheme.error;
return Container(

@ -1,5 +1,7 @@
{
"bg": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -72,10 +74,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -205,6 +203,8 @@
],
"de": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -277,10 +277,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -410,6 +406,8 @@
],
"es": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -482,10 +480,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -615,6 +609,8 @@
],
"fr": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -687,10 +683,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -820,6 +812,8 @@
],
"hu": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -892,10 +886,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -1025,6 +1015,8 @@
],
"it": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -1097,10 +1089,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -1230,6 +1218,8 @@
],
"ja": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -1302,10 +1292,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -1435,6 +1421,8 @@
],
"ko": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -1507,10 +1495,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -1640,6 +1624,8 @@
],
"nl": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -1712,10 +1698,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -1845,6 +1827,8 @@
],
"pl": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -1917,10 +1901,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -2050,6 +2030,8 @@
],
"pt": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -2122,10 +2104,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -2255,6 +2233,8 @@
],
"ru": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -2327,10 +2307,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -2460,6 +2436,8 @@
],
"sk": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -2532,10 +2510,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -2665,6 +2639,8 @@
],
"sl": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -2737,10 +2713,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -2870,6 +2842,8 @@
],
"sv": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -2942,10 +2916,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -3075,6 +3045,8 @@
],
"uk": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -3147,10 +3119,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",
@ -3280,6 +3248,8 @@
],
"zh": [
"scanner_bleReleaseWarning",
"scanner_bleReleaseWarningMessage",
"settings_messageSettings",
"settings_messageSettingsSubtitle",
"settings_gpsStatusTitle",
@ -3352,10 +3322,6 @@
"path_suggestedRoute",
"path_searchRepeaters",
"repeater_cliCommandTimeout",
"repeater_cliLateResponse",
"repeater_cliUnmatchedResponse",
"repeater_lateResponseDiscarded",
"repeater_lateResponseReceived",
"channelPath_routeDirect",
"channelPath_routeFlood",
"channelPath_snrLabel",

Loading…
Cancel
Save

Powered by TurnKey Linux.