From 1f79525025e6e7e1ca68f899eb846176839ba5e3 Mon Sep 17 00:00:00 2001 From: Christian Lauinger Date: Fri, 10 Apr 2026 09:28:47 +0200 Subject: [PATCH 1/2] Refines test mode with descriptive profiles and UI refresh Replaces generic test device labels with specific scenarios such as "Error conditions," "Charging battery," and "Low battery" to facilitate easier testing. The headset control service now handles test profiles directly, and changing the mode in settings triggers an immediate status refresh. --- HeadsetControl-MacOSTray/AppDelegate.swift | 6 +- HeadsetControl-MacOSTray/BuildNumber.xcconfig | 2 +- .../HeadsetControlService.swift | 10 ++ HeadsetControl-MacOSTray/SettingsView.swift | 17 ++- .../de.lproj/Localizable.strings | 120 +++++++++--------- .../en.lproj/Localizable.strings | 120 +++++++++--------- .../es.lproj/Localizable.strings | 120 +++++++++--------- .../fr.lproj/Localizable.strings | 120 +++++++++--------- 8 files changed, 263 insertions(+), 252 deletions(-) diff --git a/HeadsetControl-MacOSTray/AppDelegate.swift b/HeadsetControl-MacOSTray/AppDelegate.swift index 0a2d4ab..55ac87b 100644 --- a/HeadsetControl-MacOSTray/AppDelegate.swift +++ b/HeadsetControl-MacOSTray/AppDelegate.swift @@ -22,10 +22,8 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele private func activeHeadsetControlProvider() -> HeadsetControlProviding { let testMode = UserDefaults.standard.integer(forKey: "testMode") - if testMode == 0 { - return headsetControlService - } - return MockHeadsetControlService(deviceIndex: testMode) + headsetControlService.setTestProfile(testMode) + return headsetControlService } private func runControlAction(_ action: @escaping (HeadsetControlProviding) -> Void) { diff --git a/HeadsetControl-MacOSTray/BuildNumber.xcconfig b/HeadsetControl-MacOSTray/BuildNumber.xcconfig index a4dab37..9201821 100644 --- a/HeadsetControl-MacOSTray/BuildNumber.xcconfig +++ b/HeadsetControl-MacOSTray/BuildNumber.xcconfig @@ -1 +1 @@ -CURRENT_PROJECT_VERSION = 260328.0834 +CURRENT_PROJECT_VERSION = 260410.0922 diff --git a/HeadsetControl-MacOSTray/HeadsetControlService.swift b/HeadsetControl-MacOSTray/HeadsetControlService.swift index 8ac53fc..0595e02 100644 --- a/HeadsetControl-MacOSTray/HeadsetControlService.swift +++ b/HeadsetControl-MacOSTray/HeadsetControlService.swift @@ -76,6 +76,16 @@ private func legacyBatteryStatusString(_ status: hsc_battery_status_t) -> String final class HeadsetControlService: HeadsetControlProviding { private let libraryLock = NSLock() + func setTestProfile(_ profile: Int) { + let normalizedProfile = max(0, profile) + + libraryLock.lock() + defer { libraryLock.unlock() } + + hsc_set_test_profile(Int32(normalizedProfile)) + hsc_enable_test_device(normalizedProfile != 0) + } + func fetchDevices() -> [[String: Any]] { return withDiscoveredHeadsets { headsets in var devices: [[String: Any]] = [] diff --git a/HeadsetControl-MacOSTray/SettingsView.swift b/HeadsetControl-MacOSTray/SettingsView.swift index 71493bd..54ef701 100644 --- a/HeadsetControl-MacOSTray/SettingsView.swift +++ b/HeadsetControl-MacOSTray/SettingsView.swift @@ -146,24 +146,27 @@ struct SettingsView: View { HStack(alignment: .center) { Text(NSLocalizedString("Test Mode:", comment: "Test mode label")) Picker("", selection: $testMode) { - Text(NSLocalizedString("Device 1", comment: "Test mode device 1")) + Text(NSLocalizedString("1 - Error conditions", comment: "Test mode 1")) .tag(1) - Text(NSLocalizedString("Device 2", comment: "Test mode device 2")) + Text(NSLocalizedString("2 - Charging battery", comment: "Test mode 2")) .tag(2) - Text(NSLocalizedString("Device 3", comment: "Test mode device 3")) + Text(NSLocalizedString("3 - Basic battery", comment: "Test mode 3")) .tag(3) - Text(NSLocalizedString("Device 4", comment: "Test mode device 4")) + Text(NSLocalizedString("4 - Battery unavailable", comment: "Test mode 4")) .tag(4) - Text(NSLocalizedString("Device 5", comment: "Test mode device 5")) + Text(NSLocalizedString("5 - Timeout", comment: "Test mode 5")) .tag(5) - Text(NSLocalizedString("Device 6", comment: "Test mode device 6")) + Text(NSLocalizedString("6 - Full battery", comment: "Test mode 6")) .tag(6) - Text(NSLocalizedString("Device 7", comment: "Test mode device 7")) + Text(NSLocalizedString("7 - Low battery", comment: "Test mode 7")) .tag(7) Text(NSLocalizedString("Disabled", comment: "Test mode disabled")) .tag(0) } .pickerStyle(.menu) + .onChange(of: testMode) { _, _ in + NotificationCenter.default.post(name: .refreshHeadsetStatus, object: nil) + } } HStack(alignment: .center, spacing: 8) { diff --git a/HeadsetControl-MacOSTray/de.lproj/Localizable.strings b/HeadsetControl-MacOSTray/de.lproj/Localizable.strings index 8e7e205..a23ee19 100644 --- a/HeadsetControl-MacOSTray/de.lproj/Localizable.strings +++ b/HeadsetControl-MacOSTray/de.lproj/Localizable.strings @@ -1,75 +1,75 @@ -"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; -"Headset" = "Headset"; -"General Settings" = "Allgemeine Einstellungen"; -"Test Mode:" = "Testmodus:"; -"Disabled" = "Deaktiviert"; -"Update Interval (seconds):" = "Aktualisierungsintervall (Sekunden):"; "%d s" = "%d s"; -"Sidetone" = "Mithörton"; -"Sidetone Level Values" = "Mithörton-Werte"; -"Valid range: -1...128. Use -1 to hide a menu entry." = "Gültiger Bereich: -1...128. Mit -1 wird ein Menüeintrag ausgeblendet."; -"Off:" = "Aus:"; -"Off" = "Aus"; -"Low:" = "Niedrig:"; -"Low" = "Niedrig"; -"Medium:" = "Mittel:"; -"Medium" = "Mittel"; -"High:" = "Hoch:"; -"High" = "Hoch"; -"Maximum:" = "Maximal:"; -"Maximum" = "Maximal"; -"Refresh" = "Aktualisieren"; -"Close" = "Schließen"; -"No devices found" = "Keine Geräte gefunden"; -"Settings..." = "Einstellungen..."; -"Quit" = "Beenden"; -"Device" = "Gerät"; -"Unknown Device" = "Unbekanntes Gerät"; -"Vendor" = "Hersteller"; -"Unknown Vendor" = "Unbekannter Hersteller"; -"Product" = "Produkt"; -"Unknown Product" = "Unbekanntes Produkt"; -"Battery" = "Batterie"; -"Chatmix" = "Chatmix"; -"Lights" = "LED"; -"Inactive Time" = "Inaktivitätszeit"; -"Voice Prompts" = "Sprachansagen"; -"Rotate to Mute" = "Drehen zum Stummschalten"; -"Equalizer Preset" = "Equalizer-Voreinstellung"; -"Equalizer" = "Equalizer"; -"On" = "An"; +"%dh" = "%d Std."; +"(Off is always included.)" = "(Aus ist immer enthalten.)"; +"1 - Error conditions" = "1 - Fehlerzustände"; "1 Minute" = "1 Minute"; -"2 Minutes" = "2 Minuten"; -"5 Minutes" = "5 Minuten"; "10 Minutes" = "10 Minuten"; "15 Minutes" = "15 Minuten"; +"2 - Charging battery" = "2 - Akku wird geladen"; +"2 Minutes" = "2 Minuten"; +"3 - Basic battery" = "3 - Einfacher Batteriestatus"; "30 Minutes" = "30 Minuten"; +"4 - Battery unavailable" = "4 - Batterie nicht verfügbar"; "45 Minutes" = "45 Minuten"; +"5 - Timeout" = "5 - Zeitüberschreitung"; +"5 Minutes" = "5 Minuten"; +"6 - Full battery" = "6 - Voller Akku"; "60 Minutes" = "60 Minuten"; +"7 - Low battery" = "7 - Niedriger Batteriestand"; "75 Minutes" = "75 Minuten"; "90 Minutes" = "90 Minuten"; +"<1h" = "<1 Std."; +"About" = "Über"; +"Battery" = "Batterie"; +"Build" = "Build"; +"Chatmix" = "Chatmix"; +"Close" = "Schließen"; +"Comma-separated list of preset names." = "Kommagetrennte Liste von Voreinstellungsnamen."; +"Device" = "Gerät"; +"Disabled" = "Deaktiviert"; +"Equalizer" = "Equalizer"; +"Equalizer Preset" = "Equalizer-Voreinstellung"; +"Equalizer Presets" = "Equalizer-Voreinstellungen"; +"General" = "Allgemein"; +"General Settings" = "Allgemeine Einstellungen"; +"GitHub Repository" = "GitHub-Repository"; +"Headset" = "Headset"; +"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; +"High" = "Hoch"; +"High:" = "Hoch:"; +"Inactive Time" = "Inaktivitätszeit"; +"Lights" = "LED"; +"Low" = "Niedrig"; +"Low battery notification message" = "Batteriestand ist niedrig (%d%%). Bitte laden Sie Ihr Headset auf."; +"Low:" = "Niedrig:"; +"Maximum" = "Maximal"; +"Maximum:" = "Maximal:"; +"Medium" = "Mittel"; +"Medium:" = "Mittel:"; +"No devices found" = "Keine Geräte gefunden"; +"Notification on low battery" = "Benachrichtigung bei niedrigem Batteriestand"; +"Off" = "Aus"; +"Off:" = "Aus:"; +"On" = "An"; "Preset 1" = "Voreinstellung 1"; "Preset 2" = "Voreinstellung 2"; "Preset 3" = "Voreinstellung 3"; "Preset 4" = "Voreinstellung 4"; "Preset names" = "Voreinstellungsnamen"; -"Comma-separated list of preset names." = "Kommagetrennte Liste von Voreinstellungsnamen."; -"Notification on low battery" = "Benachrichtigung bei niedrigem Batteriestand"; -"Low battery notification message" = "Batteriestand ist niedrig (%d%%). Bitte laden Sie Ihr Headset auf."; -"Device 1" = "Gerät 1"; -"Device 2" = "Gerät 2"; -"Device 3" = "Gerät 3"; -"Device 4" = "Gerät 4"; -"Device 5" = "Gerät 5"; -"Device 6" = "Gerät 6"; -"Device 7" = "Gerät 7"; -"Equalizer Presets" = "Equalizer-Voreinstellungen"; -"<1h" = "<1 Std."; -"%dh" = "%d Std."; -"General" = "Allgemein"; -"About" = "Über"; -"(Off is always included.)" = "(Aus ist immer enthalten.)"; -"GitHub Repository" = "GitHub-Repository"; -"Version" = "Version"; -"Build" = "Build"; +"Product" = "Produkt"; +"Quit" = "Beenden"; +"Refresh" = "Aktualisieren"; +"Rotate to Mute" = "Drehen zum Stummschalten"; "Settings or main app window" = "Einstellungs- oder Hauptfenster der App"; +"Settings..." = "Einstellungen..."; +"Sidetone" = "Mithörton"; +"Sidetone Level Values" = "Mithörton-Werte"; +"Test Mode:" = "Testmodus:"; +"Unknown Device" = "Unbekanntes Gerät"; +"Unknown Product" = "Unbekanntes Produkt"; +"Unknown Vendor" = "Unbekannter Hersteller"; +"Update Interval (seconds):" = "Aktualisierungsintervall (Sekunden):"; +"Valid range: -1...128. Use -1 to hide a menu entry." = "Gültiger Bereich: -1...128. Mit -1 wird ein Menüeintrag ausgeblendet."; +"Vendor" = "Hersteller"; +"Version" = "Version"; +"Voice Prompts" = "Sprachansagen"; diff --git a/HeadsetControl-MacOSTray/en.lproj/Localizable.strings b/HeadsetControl-MacOSTray/en.lproj/Localizable.strings index 1666064..67dc8cf 100644 --- a/HeadsetControl-MacOSTray/en.lproj/Localizable.strings +++ b/HeadsetControl-MacOSTray/en.lproj/Localizable.strings @@ -1,75 +1,75 @@ -"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; -"Headset" = "Headset"; -"General Settings" = "General Settings"; -"Test Mode:" = "Test Mode:"; -"Disabled" = "Disabled"; -"Update Interval (seconds):" = "Update Interval (seconds):"; "%d s" = "%d s"; -"Sidetone" = "Sidetone"; -"Sidetone Level Values" = "Sidetone Level Values"; -"Valid range: -1...128. Use -1 to hide a menu entry." = "Valid range: -1...128. Use -1 to hide a menu entry."; -"Off:" = "Off:"; -"Off" = "Off"; -"Low:" = "Low:"; -"Low" = "Low"; -"Medium:" = "Medium:"; -"Medium" = "Medium"; -"High:" = "High:"; -"High" = "High"; -"Maximum:" = "Maximum:"; -"Maximum" = "Maximum"; -"Refresh" = "Refresh"; -"Close" = "Close"; -"No devices found" = "No devices found"; -"Settings..." = "Settings..."; -"Quit" = "Quit"; -"Device" = "Device"; -"Unknown Device" = "Unknown Device"; -"Vendor" = "Vendor"; -"Unknown Vendor" = "Unknown Vendor"; -"Product" = "Product"; -"Unknown Product" = "Unknown Product"; -"Battery" = "Battery"; -"Chatmix" = "Chatmix"; -"Lights" = "Lights"; -"Inactive Time" = "Inactive Time"; -"Voice Prompts" = "Voice Prompts"; -"Rotate to Mute" = "Rotate to Mute"; -"Equalizer Preset" = "Equalizer Preset"; -"Equalizer" = "Equalizer"; -"On" = "On"; +"%dh" = "%dh"; +"(Off is always included.)" = "(Off is always included.)"; +"1 - Error conditions" = "1 - Error conditions"; "1 Minute" = "1 Minute"; -"2 Minutes" = "2 Minutes"; -"5 Minutes" = "5 Minutes"; "10 Minutes" = "10 Minutes"; "15 Minutes" = "15 Minutes"; +"2 - Charging battery" = "2 - Charging battery"; +"2 Minutes" = "2 Minutes"; +"3 - Basic battery" = "3 - Basic battery"; "30 Minutes" = "30 Minutes"; +"4 - Battery unavailable" = "4 - Battery unavailable"; "45 Minutes" = "45 Minutes"; +"5 - Timeout" = "5 - Timeout"; +"5 Minutes" = "5 Minutes"; +"6 - Full battery" = "6 - Full battery"; "60 Minutes" = "60 Minutes"; +"7 - Low battery" = "7 - Low battery"; "75 Minutes" = "75 Minutes"; "90 Minutes" = "90 Minutes"; +"<1h" = "<1h"; +"About" = "About"; +"Battery" = "Battery"; +"Build" = "Build"; +"Chatmix" = "Chatmix"; +"Close" = "Close"; +"Comma-separated list of preset names." = "Comma-separated list of preset names."; +"Device" = "Device"; +"Disabled" = "Disabled"; +"Equalizer" = "Equalizer"; +"Equalizer Preset" = "Equalizer Preset"; +"Equalizer Presets" = "Equalizer Presets"; +"General" = "General"; +"General Settings" = "General Settings"; +"GitHub Repository" = "GitHub Repository"; +"Headset" = "Headset"; +"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; +"High" = "High"; +"High:" = "High:"; +"Inactive Time" = "Inactive Time"; +"Lights" = "Lights"; +"Low" = "Low"; +"Low battery notification message" = "Battery level is low (%d%%). Please charge your headset."; +"Low:" = "Low:"; +"Maximum" = "Maximum"; +"Maximum:" = "Maximum:"; +"Medium" = "Medium"; +"Medium:" = "Medium:"; +"No devices found" = "No devices found"; +"Notification on low battery" = "Notification on low battery"; +"Off" = "Off"; +"Off:" = "Off:"; +"On" = "On"; "Preset 1" = "Preset 1"; "Preset 2" = "Preset 2"; "Preset 3" = "Preset 3"; "Preset 4" = "Preset 4"; "Preset names" = "Preset names"; -"Comma-separated list of preset names." = "Comma-separated list of preset names."; -"Notification on low battery" = "Notification on low battery"; -"Low battery notification message" = "Battery level is low (%d%%). Please charge your headset."; -"Device 1" = "Device 1"; -"Device 2" = "Device 2"; -"Device 3" = "Device 3"; -"Device 4" = "Device 4"; -"Device 5" = "Device 5"; -"Device 6" = "Device 6"; -"Device 7" = "Device 7"; -"Equalizer Presets" = "Equalizer Presets"; -"<1h" = "<1h"; -"%dh" = "%dh"; -"General" = "General"; -"About" = "About"; -"(Off is always included.)" = "(Off is always included.)"; -"GitHub Repository" = "GitHub Repository"; -"Version" = "Version"; -"Build" = "Build"; +"Product" = "Product"; +"Quit" = "Quit"; +"Refresh" = "Refresh"; +"Rotate to Mute" = "Rotate to Mute"; "Settings or main app window" = "Settings or main app window"; +"Settings..." = "Settings..."; +"Sidetone" = "Sidetone"; +"Sidetone Level Values" = "Sidetone Level Values"; +"Test Mode:" = "Test Mode:"; +"Unknown Device" = "Unknown Device"; +"Unknown Product" = "Unknown Product"; +"Unknown Vendor" = "Unknown Vendor"; +"Update Interval (seconds):" = "Update Interval (seconds):"; +"Valid range: -1...128. Use -1 to hide a menu entry." = "Valid range: -1...128. Use -1 to hide a menu entry."; +"Vendor" = "Vendor"; +"Version" = "Version"; +"Voice Prompts" = "Voice Prompts"; diff --git a/HeadsetControl-MacOSTray/es.lproj/Localizable.strings b/HeadsetControl-MacOSTray/es.lproj/Localizable.strings index 00ebf31..782e6d7 100644 --- a/HeadsetControl-MacOSTray/es.lproj/Localizable.strings +++ b/HeadsetControl-MacOSTray/es.lproj/Localizable.strings @@ -1,75 +1,75 @@ -"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; -"Headset" = "Auriculares"; -"General Settings" = "Ajustes generales"; -"Test Mode:" = "Modo de prueba:"; -"Disabled" = "Desactivado"; -"Update Interval (seconds):" = "Intervalo de actualización (segundos):"; "%d s" = "%d s"; -"Sidetone" = "Retorno del micrófono"; -"Sidetone Level Values" = "Valores del retorno del micrófono"; -"Valid range: -1...128. Use -1 to hide a menu entry." = "Rango válido: -1...128. Usa -1 para ocultar una entrada del menú."; -"Off:" = "Desactivado:"; -"Off" = "Desactivado"; -"Low:" = "Bajo:"; -"Low" = "Bajo"; -"Medium:" = "Medio:"; -"Medium" = "Medio"; -"High:" = "Alto:"; -"High" = "Alto"; -"Maximum:" = "Máximo:"; -"Maximum" = "Máximo"; -"Refresh" = "Actualizar"; -"Close" = "Cerrar"; -"No devices found" = "No se encontraron dispositivos"; -"Settings..." = "Ajustes..."; -"Quit" = "Salir"; -"Device" = "Dispositivo"; -"Unknown Device" = "Dispositivo desconocido"; -"Vendor" = "Fabricante"; -"Unknown Vendor" = "Fabricante desconocido"; -"Product" = "Producto"; -"Unknown Product" = "Producto desconocido"; -"Battery" = "Batería"; -"Chatmix" = "Chatmix"; -"Lights" = "Luces"; -"Inactive Time" = "Tiempo de inactividad"; -"Voice Prompts" = "Indicaciones de voz"; -"Rotate to Mute" = "Girar para silenciar"; -"Equalizer Preset" = "Preajuste del ecualizador"; -"Equalizer" = "Ecualizador"; -"On" = "Activado"; +"%dh" = "%d h"; +"(Off is always included.)" = "(La opción Desactivado siempre está incluida.)"; +"1 - Error conditions" = "1 - Condiciones de error"; "1 Minute" = "1 minuto"; -"2 Minutes" = "2 minutos"; -"5 Minutes" = "5 minutos"; "10 Minutes" = "10 minutos"; "15 Minutes" = "15 minutos"; +"2 - Charging battery" = "2 - Batería cargándose"; +"2 Minutes" = "2 minutos"; +"3 - Basic battery" = "3 - Estado básico de batería"; "30 Minutes" = "30 minutos"; +"4 - Battery unavailable" = "4 - Batería no disponible"; "45 Minutes" = "45 minutos"; +"5 - Timeout" = "5 - Tiempo de espera agotado"; +"5 Minutes" = "5 minutos"; +"6 - Full battery" = "6 - Batería completa"; "60 Minutes" = "60 minutos"; +"7 - Low battery" = "7 - Batería baja"; "75 Minutes" = "75 minutos"; "90 Minutes" = "90 minutos"; +"<1h" = "<1 h"; +"About" = "Acerca de"; +"Battery" = "Batería"; +"Build" = "Compilación"; +"Chatmix" = "Chatmix"; +"Close" = "Cerrar"; +"Comma-separated list of preset names." = "Lista de nombres de preajustes separados por comas."; +"Device" = "Dispositivo"; +"Disabled" = "Desactivado"; +"Equalizer" = "Ecualizador"; +"Equalizer Preset" = "Preajuste del ecualizador"; +"Equalizer Presets" = "Preajustes del ecualizador"; +"General" = "General"; +"General Settings" = "Ajustes generales"; +"GitHub Repository" = "Repositorio de GitHub"; +"Headset" = "Auriculares"; +"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; +"High" = "Alto"; +"High:" = "Alto:"; +"Inactive Time" = "Tiempo de inactividad"; +"Lights" = "Luces"; +"Low" = "Bajo"; +"Low battery notification message" = "El nivel de batería es bajo (%d%%). Por favor, carga los auriculares."; +"Low:" = "Bajo:"; +"Maximum" = "Máximo"; +"Maximum:" = "Máximo:"; +"Medium" = "Medio"; +"Medium:" = "Medio:"; +"No devices found" = "No se encontraron dispositivos"; +"Notification on low battery" = "Notificación de batería baja"; +"Off" = "Desactivado"; +"Off:" = "Desactivado:"; +"On" = "Activado"; "Preset 1" = "Preajuste 1"; "Preset 2" = "Preajuste 2"; "Preset 3" = "Preajuste 3"; "Preset 4" = "Preajuste 4"; "Preset names" = "Nombres de los preajustes"; -"Comma-separated list of preset names." = "Lista de nombres de preajustes separados por comas."; -"Notification on low battery" = "Notificación de batería baja"; -"Low battery notification message" = "El nivel de batería es bajo (%d%%). Por favor, carga los auriculares."; -"Device 1" = "Dispositivo 1"; -"Device 2" = "Dispositivo 2"; -"Device 3" = "Dispositivo 3"; -"Device 4" = "Dispositivo 4"; -"Device 5" = "Dispositivo 5"; -"Device 6" = "Dispositivo 6"; -"Device 7" = "Dispositivo 7"; -"Equalizer Presets" = "Preajustes del ecualizador"; -"<1h" = "<1 h"; -"%dh" = "%d h"; -"General" = "General"; -"About" = "Acerca de"; -"(Off is always included.)" = "(La opción Desactivado siempre está incluida.)"; -"GitHub Repository" = "Repositorio de GitHub"; -"Version" = "Versión"; -"Build" = "Compilación"; +"Product" = "Producto"; +"Quit" = "Salir"; +"Refresh" = "Actualizar"; +"Rotate to Mute" = "Girar para silenciar"; "Settings or main app window" = "Ventana de ajustes o ventana principal de la app"; +"Settings..." = "Ajustes..."; +"Sidetone" = "Retorno del micrófono"; +"Sidetone Level Values" = "Valores del retorno del micrófono"; +"Test Mode:" = "Modo de prueba:"; +"Unknown Device" = "Dispositivo desconocido"; +"Unknown Product" = "Producto desconocido"; +"Unknown Vendor" = "Fabricante desconocido"; +"Update Interval (seconds):" = "Intervalo de actualización (segundos):"; +"Valid range: -1...128. Use -1 to hide a menu entry." = "Rango válido: -1...128. Usa -1 para ocultar una entrada del menú."; +"Vendor" = "Fabricante"; +"Version" = "Versión"; +"Voice Prompts" = "Indicaciones de voz"; diff --git a/HeadsetControl-MacOSTray/fr.lproj/Localizable.strings b/HeadsetControl-MacOSTray/fr.lproj/Localizable.strings index 4ce6814..45d4693 100644 --- a/HeadsetControl-MacOSTray/fr.lproj/Localizable.strings +++ b/HeadsetControl-MacOSTray/fr.lproj/Localizable.strings @@ -1,75 +1,75 @@ -"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; -"Headset" = "Casque"; -"General Settings" = "Réglages généraux"; -"Test Mode:" = "Mode de test :"; -"Disabled" = "Désactivé"; -"Update Interval (seconds):" = "Intervalle de mise à jour (secondes) :"; "%d s" = "%d s"; -"Sidetone" = "Retour micro"; -"Sidetone Level Values" = "Valeurs du retour micro"; -"Valid range: -1...128. Use -1 to hide a menu entry." = "Plage valide : -1...128. Utilisez -1 pour masquer une entrée de menu."; -"Off:" = "Désactivé :"; -"Off" = "Désactivé"; -"Low:" = "Faible :"; -"Low" = "Faible"; -"Medium:" = "Moyen :"; -"Medium" = "Moyen"; -"High:" = "Élevé :"; -"High" = "Élevé"; -"Maximum:" = "Maximum :"; -"Maximum" = "Maximum"; -"Refresh" = "Actualiser"; -"Close" = "Fermer"; -"No devices found" = "Aucun appareil trouvé"; -"Settings..." = "Réglages..."; -"Quit" = "Quitter"; -"Device" = "Appareil"; -"Unknown Device" = "Appareil inconnu"; -"Vendor" = "Fabricant"; -"Unknown Vendor" = "Fabricant inconnu"; -"Product" = "Produit"; -"Unknown Product" = "Produit inconnu"; -"Battery" = "Batterie"; -"Chatmix" = "Chatmix"; -"Lights" = "Éclairage"; -"Inactive Time" = "Temps d'inactivité"; -"Voice Prompts" = "Invites vocales"; -"Rotate to Mute" = "Pivoter pour couper le micro"; -"Equalizer Preset" = "Préréglage de l'égaliseur"; -"Equalizer" = "Égaliseur"; -"On" = "Activé"; +"%dh" = "%d h"; +"(Off is always included.)" = "(Désactivé est toujours inclus.)"; +"1 - Error conditions" = "1 - Conditions d'erreur"; "1 Minute" = "1 minute"; -"2 Minutes" = "2 minutes"; -"5 Minutes" = "5 minutes"; "10 Minutes" = "10 minutes"; "15 Minutes" = "15 minutes"; +"2 - Charging battery" = "2 - Batterie en charge"; +"2 Minutes" = "2 minutes"; +"3 - Basic battery" = "3 - État de batterie basique"; "30 Minutes" = "30 minutes"; +"4 - Battery unavailable" = "4 - Batterie indisponible"; "45 Minutes" = "45 minutes"; +"5 - Timeout" = "5 - Délai dépassé"; +"5 Minutes" = "5 minutes"; +"6 - Full battery" = "6 - Batterie pleine"; "60 Minutes" = "60 minutes"; +"7 - Low battery" = "7 - Batterie faible"; "75 Minutes" = "75 minutes"; "90 Minutes" = "90 minutes"; +"<1h" = "<1 h"; +"About" = "À propos"; +"Battery" = "Batterie"; +"Build" = "Build"; +"Chatmix" = "Chatmix"; +"Close" = "Fermer"; +"Comma-separated list of preset names." = "Liste de noms de préréglages séparés par des virgules."; +"Device" = "Appareil"; +"Disabled" = "Désactivé"; +"Equalizer" = "Égaliseur"; +"Equalizer Preset" = "Préréglage de l'égaliseur"; +"Equalizer Presets" = "Préréglages de l'égaliseur"; +"General" = "Général"; +"General Settings" = "Réglages généraux"; +"GitHub Repository" = "Dépôt GitHub"; +"Headset" = "Casque"; +"HeadsetControl-MacOSTray" = "HeadsetControl-MacOSTray"; +"High" = "Élevé"; +"High:" = "Élevé :"; +"Inactive Time" = "Temps d'inactivité"; +"Lights" = "Éclairage"; +"Low" = "Faible"; +"Low battery notification message" = "Le niveau de batterie est faible (%d%%). Veuillez recharger votre casque."; +"Low:" = "Faible :"; +"Maximum" = "Maximum"; +"Maximum:" = "Maximum :"; +"Medium" = "Moyen"; +"Medium:" = "Moyen :"; +"No devices found" = "Aucun appareil trouvé"; +"Notification on low battery" = "Notification en cas de batterie faible"; +"Off" = "Désactivé"; +"Off:" = "Désactivé :"; +"On" = "Activé"; "Preset 1" = "Préréglage 1"; "Preset 2" = "Préréglage 2"; "Preset 3" = "Préréglage 3"; "Preset 4" = "Préréglage 4"; "Preset names" = "Noms des préréglages"; -"Comma-separated list of preset names." = "Liste de noms de préréglages séparés par des virgules."; -"Notification on low battery" = "Notification en cas de batterie faible"; -"Low battery notification message" = "Le niveau de batterie est faible (%d%%). Veuillez recharger votre casque."; -"Device 1" = "Appareil 1"; -"Device 2" = "Appareil 2"; -"Device 3" = "Appareil 3"; -"Device 4" = "Appareil 4"; -"Device 5" = "Appareil 5"; -"Device 6" = "Appareil 6"; -"Device 7" = "Appareil 7"; -"Equalizer Presets" = "Préréglages de l'égaliseur"; -"<1h" = "<1 h"; -"%dh" = "%d h"; -"General" = "Général"; -"About" = "À propos"; -"(Off is always included.)" = "(Désactivé est toujours inclus.)"; -"GitHub Repository" = "Dépôt GitHub"; -"Version" = "Version"; -"Build" = "Build"; +"Product" = "Produit"; +"Quit" = "Quitter"; +"Refresh" = "Actualiser"; +"Rotate to Mute" = "Pivoter pour couper le micro"; "Settings or main app window" = "Fenêtre des réglages ou fenêtre principale de l'app"; +"Settings..." = "Réglages..."; +"Sidetone" = "Retour micro"; +"Sidetone Level Values" = "Valeurs du retour micro"; +"Test Mode:" = "Mode de test :"; +"Unknown Device" = "Appareil inconnu"; +"Unknown Product" = "Produit inconnu"; +"Unknown Vendor" = "Fabricant inconnu"; +"Update Interval (seconds):" = "Intervalle de mise à jour (secondes) :"; +"Valid range: -1...128. Use -1 to hide a menu entry." = "Plage valide : -1...128. Utilisez -1 pour masquer une entrée de menu."; +"Vendor" = "Fabricant"; +"Version" = "Version"; +"Voice Prompts" = "Invites vocales"; From 30a54525e02308fd7e755caa2157729f6a4205a5 Mon Sep 17 00:00:00 2001 From: Christian Lauinger Date: Fri, 10 Apr 2026 09:51:09 +0200 Subject: [PATCH 2/2] Improves battery status display with charging indicators and error handling MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds a charging icon (⚡︎) to the menu and tray status when the headset is plugged in. This update also refactors battery text generation into a helper method and expands the service layer to recognize error and timeout states. --- HeadsetControl-MacOSTray/AppDelegate.swift | 37 +++++++++++++------ HeadsetControl-MacOSTray/BuildNumber.xcconfig | 2 +- .../HeadsetControlService.swift | 18 ++++++--- 3 files changed, 39 insertions(+), 18 deletions(-) diff --git a/HeadsetControl-MacOSTray/AppDelegate.swift b/HeadsetControl-MacOSTray/AppDelegate.swift index 55ac87b..90402e3 100644 --- a/HeadsetControl-MacOSTray/AppDelegate.swift +++ b/HeadsetControl-MacOSTray/AppDelegate.swift @@ -185,17 +185,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele let devicesResult = provider.fetchDevices() var batteryLevelText: String? = nil if let device = devicesResult.first, - let battery = device["battery"] as? [String: Any], - let level = battery["level"] as? Int { - batteryLevelText = "\(level)%" + let battery = device["battery"] as? [String: Any] { + batteryLevelText = self.batteryChargeText(from: battery) let status = battery["status"] as? String ?? "" - let notifyOnLowBattery = UserDefaults.standard.bool(forKey: "notifyOnLowBattery") - if notifyOnLowBattery && ((status == "BATTERY_AVAILABLE" && level <= 25)) && !self.lowBatteryNotificationShown { - self.showLowBatteryNotification(level: level) - self.lowBatteryNotificationShown = true - } - if status == "BATTERY_AVAILABLE" && level > 25 && UserDefaults.standard.integer(forKey: "testMode") == 0 { - self.lowBatteryNotificationShown = false + if let level = battery["level"] as? Int { + let notifyOnLowBattery = UserDefaults.standard.bool(forKey: "notifyOnLowBattery") + if notifyOnLowBattery && ((status == "BATTERY_AVAILABLE" && level <= 25)) && !self.lowBatteryNotificationShown { + self.showLowBatteryNotification(level: level) + self.lowBatteryNotificationShown = true + } + if status == "BATTERY_AVAILABLE" && level > 25 && UserDefaults.standard.integer(forKey: "testMode") == 0 { + self.lowBatteryNotificationShown = false + } } } DispatchQueue.main.async { @@ -211,6 +212,18 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele } } + private func batteryChargeText(from battery: [String: Any]) -> String? { + let status = battery["status"] as? String ?? "" + let isCharging = status == "BATTERY_CHARGING" + let prefix = isCharging ? "⚡︎ " : "" + + if let level = battery["level"] as? Int, level >= 0 { + return prefix + "\(level)%" + } + + return isCharging ? "⚡︎" : nil + } + // Helper to format time_to_empty_min into a submenu suffix like " (5h)" or " (<1h)". // - Accepts Int/Double/String values from JSON and returns an optional suffix with a leading space. private func formatTimeToEmpty(minutesAny: Any?) -> String? { @@ -288,10 +301,10 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSMenuDelegate, NSWindowDele menu.addItem(withTitle: String(format: "%@: %@", NSLocalizedString("Device", comment: "Device label"), deviceName), action: nil, keyEquivalent: "") menu.addItem(withTitle: String(format: "%@: %@", NSLocalizedString("Vendor", comment: "Vendor label"), vendor), action: nil, keyEquivalent: "") menu.addItem(withTitle: String(format: "%@: %@", NSLocalizedString("Product", comment: "Product label"), product), action: nil, keyEquivalent: "") - if let battery = device["battery"] as? [String: Any], let level = battery["level"] as? Int { + if let battery = device["battery"] as? [String: Any], let batteryText = batteryChargeText(from: battery) { // Append time-to-empty in hours (submenu only) when available. Use floor rounding and "h" suffix; show "<1h" for under 60 minutes. let suffix = formatTimeToEmpty(minutesAny: battery["time_to_empty_min"]) - let title = String(format: "%@: %d%%%@", NSLocalizedString("Battery", comment: "Battery label"), level, suffix ?? "") + let title = String(format: "%@: %@%@", NSLocalizedString("Battery", comment: "Battery label"), batteryText, suffix ?? "") menu.addItem(withTitle: title, action: nil, keyEquivalent: "") } if let chatmix = device["chatmix"] { diff --git a/HeadsetControl-MacOSTray/BuildNumber.xcconfig b/HeadsetControl-MacOSTray/BuildNumber.xcconfig index 9201821..2cf6c84 100644 --- a/HeadsetControl-MacOSTray/BuildNumber.xcconfig +++ b/HeadsetControl-MacOSTray/BuildNumber.xcconfig @@ -1 +1 @@ -CURRENT_PROJECT_VERSION = 260410.0922 +CURRENT_PROJECT_VERSION = 260410.0948 diff --git a/HeadsetControl-MacOSTray/HeadsetControlService.swift b/HeadsetControl-MacOSTray/HeadsetControlService.swift index 0595e02..ed5a556 100644 --- a/HeadsetControl-MacOSTray/HeadsetControlService.swift +++ b/HeadsetControl-MacOSTray/HeadsetControlService.swift @@ -65,11 +65,19 @@ struct HeadsetCapability { } private func legacyBatteryStatusString(_ status: hsc_battery_status_t) -> String? { - switch status { - case HSC_BATTERY_AVAILABLE: return "BATTERY_AVAILABLE" - case HSC_BATTERY_CHARGING: return "BATTERY_CHARGING" - case HSC_BATTERY_UNAVAILABLE: return "BATTERY_UNAVAILABLE" - default: return nil + switch status.rawValue { + case HSC_BATTERY_AVAILABLE.rawValue: + return "BATTERY_AVAILABLE" + case HSC_BATTERY_CHARGING.rawValue, 1: + return "BATTERY_CHARGING" + case HSC_BATTERY_UNAVAILABLE.rawValue: + return "BATTERY_UNAVAILABLE" + case HSC_BATTERY_ERROR.rawValue: + return "BATTERY_ERROR" + case HSC_BATTERY_TIMEOUT.rawValue: + return "BATTERY_TIMEOUT" + default: + return nil } }