From 13e38230c666b25369830034d6acffca85dc9103 Mon Sep 17 00:00:00 2001 From: Strycher Date: Sat, 27 Jun 2026 01:00:35 -0400 Subject: [PATCH] feat(#134): show firmware version + model in Device Info MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Parse the device-info reply's readable strings (build date / model / firmware version, NUL-terminated after the 8-byte header) and show firmwareVersion + deviceModel in Settings → Device Info. Raw strings are logged on connect. End-anchored field mapping (deviceInfoFields) keeps the version correct on partial frames. Gemini (fix-then-ship): removed the unused deviceBuildDate field/getter (dead state) — the build date stays in the connect log via the raw strings, and deviceInfoFields still parses + tests it. Closes #134 Co-Authored-By: Claude Opus 4.8 --- lib/connector/meshcore_connector.dart | 21 ++++++++ lib/connector/meshcore_protocol.dart | 36 +++++++++++++ lib/l10n/app_en.arb | 2 + lib/l10n/app_localizations.dart | 12 +++++ lib/l10n/app_localizations_bg.dart | 6 +++ lib/l10n/app_localizations_de.dart | 6 +++ lib/l10n/app_localizations_en.dart | 6 +++ lib/l10n/app_localizations_es.dart | 6 +++ lib/l10n/app_localizations_fr.dart | 6 +++ lib/l10n/app_localizations_hu.dart | 6 +++ lib/l10n/app_localizations_it.dart | 6 +++ lib/l10n/app_localizations_ja.dart | 6 +++ lib/l10n/app_localizations_ko.dart | 6 +++ lib/l10n/app_localizations_nl.dart | 6 +++ lib/l10n/app_localizations_pl.dart | 6 +++ lib/l10n/app_localizations_pt.dart | 6 +++ lib/l10n/app_localizations_ru.dart | 6 +++ lib/l10n/app_localizations_sk.dart | 6 +++ lib/l10n/app_localizations_sl.dart | 6 +++ lib/l10n/app_localizations_sv.dart | 6 +++ lib/l10n/app_localizations_uk.dart | 6 +++ lib/l10n/app_localizations_zh.dart | 6 +++ lib/screens/settings_screen.dart | 6 +++ test/device_info_strings_test.dart | 78 +++++++++++++++++++++++++++ untranslated.json | 34 ++++++++++++ 25 files changed, 297 insertions(+) create mode 100644 test/device_info_strings_test.dart diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart index 1f90f7e..836d351 100644 --- a/lib/connector/meshcore_connector.dart +++ b/lib/connector/meshcore_connector.dart @@ -196,6 +196,8 @@ class MeshCoreConnector extends ChangeNotifier { bool? _clientRepeat; MeshCoreRadioStateSnapshot? _rememberedNonRepeatRadioState; int? _firmwareVerCode; + String? _firmwareVersion; + String? _deviceModel; int? _offbandCaps; int _pathHashByteWidth = 1; CompanionRadioStats? _latestRadioStats; @@ -448,6 +450,12 @@ class MeshCoreConnector extends ChangeNotifier { int? get firmwareVerCode => _firmwareVerCode; + /// Readable firmware version / model from the device-info reply (#134); null + /// until a device-info frame arrives, or on older firmware that omits the + /// strings. (Build date is parsed too but only surfaced in the connect log.) + String? get firmwareVersion => _firmwareVersion; + String? get deviceModel => _deviceModel; + /// `offband_caps` capability bitfield from the device-info reply (v14+); /// null on older firmware that sends a shorter frame. int? get offbandCaps => _offbandCaps; @@ -2539,6 +2547,8 @@ class MeshCoreConnector extends ChangeNotifier { _clientRepeat = null; _rememberedNonRepeatRadioState = null; _firmwareVerCode = null; + _firmwareVersion = null; + _deviceModel = null; _batteryMillivolts = null; _repeaterBatterySnapshots.clear(); _batteryRequested = false; @@ -4115,6 +4125,17 @@ class MeshCoreConnector extends ChangeNotifier { } _firmwareVerCode = frame[1]; + // Readable build date / model / firmware version — NUL-terminated strings + // after the 8-byte header, before the binary config block. (#134) + final infoStrings = parseDeviceInfoStrings(frame); + final info = deviceInfoFields(infoStrings); + _firmwareVersion = info.version; + _deviceModel = info.model; + _appDebugLogService?.info( + 'Device info: ${infoStrings.isEmpty ? '(no strings)' : infoStrings.join(' · ')}', + tag: 'Device', + ); + // Parse client_repeat from firmware v9+ (byte 80) if (frame.length >= 81) { _clientRepeat = frame[80] != 0; diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart index 719cc2f..8b4144d 100644 --- a/lib/connector/meshcore_protocol.dart +++ b/lib/connector/meshcore_protocol.dart @@ -465,6 +465,42 @@ int? realHopCount(int? rawByteLen, int hashWidth) { return rawByteLen ~/ w; } +/// Readable strings from a RESP_CODE_DEVICE_INFO frame: build date, model, and +/// firmware version, NUL-terminated after the 8-byte header and before the +/// binary config block (client_repeat/path-hash/caps live at bytes 80-82). +/// Collects printable-ASCII runs (>=2 chars) from byte 8 up to byte 80. (#134) +List parseDeviceInfoStrings(Uint8List frame) { + final out = []; + final buf = StringBuffer(); + final end = frame.length < 80 ? frame.length : 80; + for (var i = 8; i < end; i++) { + final b = frame[i]; + if (b >= 0x20 && b < 0x7f) { + buf.writeCharCode(b); + } else { + if (buf.length >= 2) out.add(buf.toString()); + buf.clear(); + } + } + if (buf.length >= 2) out.add(buf.toString()); + return out; +} + +/// Maps a [parseDeviceInfoStrings] list to (version, model, build date), +/// anchored from the END: the firmware version is the last string and the most +/// likely to be present, model second-to-last, build date third-to-last. A +/// partial set keeps the version correct instead of shifting every field. (#134) +({String? version, String? model, String? buildDate}) deviceInfoFields( + List strings, +) { + final n = strings.length; + return ( + version: n >= 1 ? strings[n - 1] : null, + model: n >= 2 ? strings[n - 2] : null, + buildDate: n >= 3 ? strings[n - 3] : null, + ); +} + // Helper to convert uint32 to hex string String ackHashToHex(int ackHash) { return ackHash.toRadixString(16).padLeft(8, '0'); diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 42a95d8..9c7407e 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -224,6 +224,8 @@ "settings_infoName": "Name", "settings_infoId": "ID", "settings_infoStatus": "Status", + "settings_infoFirmware": "Firmware", + "settings_infoModel": "Model", "settings_infoBattery": "Battery", "settings_infoPublicKey": "Public Key", "settings_infoContactsCount": "Contacts Count", diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 8505916..e111cbe 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -1102,6 +1102,18 @@ abstract class AppLocalizations { /// **'Status'** String get settings_infoStatus; + /// No description provided for @settings_infoFirmware. + /// + /// In en, this message translates to: + /// **'Firmware'** + String get settings_infoFirmware; + + /// No description provided for @settings_infoModel. + /// + /// In en, this message translates to: + /// **'Model'** + String get settings_infoModel; + /// No description provided for @settings_infoBattery. /// /// In en, this message translates to: diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart index 255970e..e833c8b 100644 --- a/lib/l10n/app_localizations_bg.dart +++ b/lib/l10n/app_localizations_bg.dart @@ -552,6 +552,12 @@ class AppLocalizationsBg extends AppLocalizations { @override String get settings_infoStatus => 'Статус'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Батерия'; diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 9251278..c26821e 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -547,6 +547,12 @@ class AppLocalizationsDe extends AppLocalizations { @override String get settings_infoStatus => 'Status'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Akku'; diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 0d94948..f2677d9 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -537,6 +537,12 @@ class AppLocalizationsEn extends AppLocalizations { @override String get settings_infoStatus => 'Status'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Battery'; diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart index a7dc433..e51b457 100644 --- a/lib/l10n/app_localizations_es.dart +++ b/lib/l10n/app_localizations_es.dart @@ -548,6 +548,12 @@ class AppLocalizationsEs extends AppLocalizations { @override String get settings_infoStatus => 'Estado'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batería'; diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 8eb0b68..399f6be 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -552,6 +552,12 @@ class AppLocalizationsFr extends AppLocalizations { @override String get settings_infoStatus => 'État'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batterie'; diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart index 60307ae..ed3e66d 100644 --- a/lib/l10n/app_localizations_hu.dart +++ b/lib/l10n/app_localizations_hu.dart @@ -550,6 +550,12 @@ class AppLocalizationsHu extends AppLocalizations { @override String get settings_infoStatus => 'Állapot'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Akku'; diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index dcde96a..38d8b40 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -550,6 +550,12 @@ class AppLocalizationsIt extends AppLocalizations { @override String get settings_infoStatus => 'Stato'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batteria'; diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart index 07e0eac..9cdaa2f 100644 --- a/lib/l10n/app_localizations_ja.dart +++ b/lib/l10n/app_localizations_ja.dart @@ -523,6 +523,12 @@ class AppLocalizationsJa extends AppLocalizations { @override String get settings_infoStatus => 'ステータス'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'バッテリー'; diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart index 8d286b6..44c35eb 100644 --- a/lib/l10n/app_localizations_ko.dart +++ b/lib/l10n/app_localizations_ko.dart @@ -523,6 +523,12 @@ class AppLocalizationsKo extends AppLocalizations { @override String get settings_infoStatus => '상태'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => '배터리'; diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart index ae7a063..9f1af28 100644 --- a/lib/l10n/app_localizations_nl.dart +++ b/lib/l10n/app_localizations_nl.dart @@ -544,6 +544,12 @@ class AppLocalizationsNl extends AppLocalizations { @override String get settings_infoStatus => 'Status'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batterij'; diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart index 54c7029..f0e81fa 100644 --- a/lib/l10n/app_localizations_pl.dart +++ b/lib/l10n/app_localizations_pl.dart @@ -552,6 +552,12 @@ class AppLocalizationsPl extends AppLocalizations { @override String get settings_infoStatus => 'Stan'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Bateria'; diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart index da50fe9..3671f97 100644 --- a/lib/l10n/app_localizations_pt.dart +++ b/lib/l10n/app_localizations_pt.dart @@ -550,6 +550,12 @@ class AppLocalizationsPt extends AppLocalizations { @override String get settings_infoStatus => 'Estado'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Bateria'; diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart index ad2ced8..acb55d6 100644 --- a/lib/l10n/app_localizations_ru.dart +++ b/lib/l10n/app_localizations_ru.dart @@ -549,6 +549,12 @@ class AppLocalizationsRu extends AppLocalizations { @override String get settings_infoStatus => 'Статус'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Батарея'; diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart index c6fcfbd..307fc79 100644 --- a/lib/l10n/app_localizations_sk.dart +++ b/lib/l10n/app_localizations_sk.dart @@ -544,6 +544,12 @@ class AppLocalizationsSk extends AppLocalizations { @override String get settings_infoStatus => 'Stav'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batéria'; diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart index 3b5d075..55f9ff5 100644 --- a/lib/l10n/app_localizations_sl.dart +++ b/lib/l10n/app_localizations_sl.dart @@ -541,6 +541,12 @@ class AppLocalizationsSl extends AppLocalizations { @override String get settings_infoStatus => 'Stanje'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Baterija'; diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart index 160d71d..3dac7ef 100644 --- a/lib/l10n/app_localizations_sv.dart +++ b/lib/l10n/app_localizations_sv.dart @@ -539,6 +539,12 @@ class AppLocalizationsSv extends AppLocalizations { @override String get settings_infoStatus => 'Status'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Batteri'; diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart index 07a5969..1bbc53d 100644 --- a/lib/l10n/app_localizations_uk.dart +++ b/lib/l10n/app_localizations_uk.dart @@ -547,6 +547,12 @@ class AppLocalizationsUk extends AppLocalizations { @override String get settings_infoStatus => 'Статус'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => 'Батарея'; diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart index 7988d99..542e440 100644 --- a/lib/l10n/app_localizations_zh.dart +++ b/lib/l10n/app_localizations_zh.dart @@ -515,6 +515,12 @@ class AppLocalizationsZh extends AppLocalizations { @override String get settings_infoStatus => '状态'; + @override + String get settings_infoFirmware => 'Firmware'; + + @override + String get settings_infoModel => 'Model'; + @override String get settings_infoBattery => '电池'; diff --git a/lib/screens/settings_screen.dart b/lib/screens/settings_screen.dart index 2998977..5443936 100644 --- a/lib/screens/settings_screen.dart +++ b/lib/screens/settings_screen.dart @@ -369,6 +369,8 @@ class _SettingsScreenState extends State { MeshCoreConnector connector, ) { final l10n = context.l10n; + final firmwareVersion = connector.firmwareVersion; + final deviceModel = connector.deviceModel; return Card( child: Padding( @@ -384,6 +386,10 @@ class _SettingsScreenState extends State { ? l10n.common_connected : l10n.common_disconnected, ), + if (firmwareVersion != null) + _buildInfoRow(l10n.settings_infoFirmware, firmwareVersion), + if (deviceModel != null) + _buildInfoRow(l10n.settings_infoModel, deviceModel), _buildBatteryInfoRow(context, connector), if (connector.selfName != null) _buildInfoRow(l10n.settings_nodeName, connector.selfName!), diff --git a/test/device_info_strings_test.dart b/test/device_info_strings_test.dart new file mode 100644 index 0000000..a58b1f5 --- /dev/null +++ b/test/device_info_strings_test.dart @@ -0,0 +1,78 @@ +import 'dart:typed_data'; + +import 'package:flutter_test/flutter_test.dart'; +import 'package:meshcore_open/connector/meshcore_protocol.dart'; + +void main() { + group('parseDeviceInfoStrings', () { + Uint8List frameWith(List strings, {bool pad = true}) { + final frame = [0x0d, 0x0e, 0x02, 0x03, 0, 0, 0, 0]; // 8-byte header + for (final s in strings) { + frame.addAll(s.codeUnits); + frame.add(0); // NUL terminator + } + if (pad) { + while (frame.length < 80) { + frame.add(0); + } + frame.addAll([1, 1, 0]); // client_repeat, path-hash mode, caps + } + return Uint8List.fromList(frame); + } + + test('extracts build date, model, and firmware version in order', () { + final out = parseDeviceInfoStrings( + frameWith(['24-Jun-2026', 'RAK 3401', '1.7.1']), + ); + expect(out, ['24-Jun-2026', 'RAK 3401', '1.7.1']); + }); + + test('returns empty for a header-only frame', () { + expect( + parseDeviceInfoStrings(Uint8List.fromList(List.filled(8, 0))), + isEmpty, + ); + }); + + test('ignores printable bytes in the binary config block (byte 80+)', () { + final frame = [0x0d, 0x0e, 0, 0, 0, 0, 0, 0]; + frame.addAll('1.7.1'.codeUnits); + frame.add(0); + while (frame.length < 80) { + frame.add(0); + } + frame.addAll('ABCD'.codeUnits); // printable but past the scan window + expect(parseDeviceInfoStrings(Uint8List.fromList(frame)), ['1.7.1']); + }); + }); + + group('deviceInfoFields', () { + test('three strings map to build date, model, and version', () { + final f = deviceInfoFields(['24-Jun-2026', 'RAK 3401', '1.7.1']); + expect(f.version, '1.7.1'); + expect(f.model, 'RAK 3401'); + expect(f.buildDate, '24-Jun-2026'); + }); + + test('two strings keep version + model, drop build date', () { + final f = deviceInfoFields(['RAK 3401', '1.7.1']); + expect(f.version, '1.7.1'); + expect(f.model, 'RAK 3401'); + expect(f.buildDate, isNull); + }); + + test('one string is the version', () { + final f = deviceInfoFields(['1.7.1']); + expect(f.version, '1.7.1'); + expect(f.model, isNull); + expect(f.buildDate, isNull); + }); + + test('empty maps to all null', () { + final f = deviceInfoFields([]); + expect(f.version, isNull); + expect(f.model, isNull); + expect(f.buildDate, isNull); + }); + }); +} diff --git a/untranslated.json b/untranslated.json index b28dc3d..010fae4 100644 --- a/untranslated.json +++ b/untranslated.json @@ -2,6 +2,8 @@ "bg": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -14,6 +16,8 @@ "de": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -26,6 +30,8 @@ "es": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -38,6 +44,8 @@ "fr": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -50,6 +58,8 @@ "hu": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -62,6 +72,8 @@ "it": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -74,6 +86,8 @@ "ja": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -86,6 +100,8 @@ "ko": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -98,6 +114,8 @@ "nl": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -110,6 +128,8 @@ "pl": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -122,6 +142,8 @@ "pt": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -134,6 +156,8 @@ "ru": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -146,6 +170,8 @@ "sk": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -158,6 +184,8 @@ "sl": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -170,6 +198,8 @@ "sv": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -182,6 +212,8 @@ "uk": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h", @@ -194,6 +226,8 @@ "zh": [ "settings_messageSettings", "settings_messageSettingsSubtitle", + "settings_infoFirmware", + "settings_infoModel", "appSettings_clock", "appSettings_clockSystem", "appSettings_clock12h",