iobroker.govee-local 0.4.2 → 0.4.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -24,6 +24,16 @@ For **Windows** Users: Please go to the settings and make sure you choose the co
24
24
  Placeholder for the next version (at the beginning of the line):
25
25
  ### **WORK IN PROGRESS**
26
26
  -->
27
+ ### 0.4.4 (2025-11-13)
28
+
29
+ - add option to run auto discovery once on startup, in interval or completly disable it
30
+
31
+ ### 0.4.3 (2025-11-11)
32
+
33
+ - fix responsive design in the settings
34
+ - revert forbidden characters checking
35
+ - update dependencies
36
+
27
37
  ### 0.4.2 (2025-11-08)
28
38
 
29
39
  - update dependencies
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Adaptereinstellungen für govee-local",
3
- "_searchInterval": "Suchintervall",
4
- "_deviceStatusRefreshInterval": "Aktualisierungsintervall für den Gerätestatus",
5
- "_configDescription": "Hier muss nichts konfiguriert werden. Bitte die Intervalle nur ändern, wenn dies explizit gewollt ist.",
6
- "_extendedLoggingDescription": "Die erweiterte Protokollierung druckt alle vom Gerät empfangenen Daten mit Ebeneninformationen in das Protokoll. Da dies zu einem großen Log-Spam führt, wird empfohlen, die Funktion nur bei Bedarf einzuschalten und anschließend wieder auszuschalten.",
7
- "_extendedLogging": "Erweiterte Protokollierung aktiviert",
8
- "_networkInterface": "Netzwerkschnittstelle",
9
- "_networkHint": "Windows-Nutzer: Sie müssen eventuell Ihre IP-Adresse manuell auswählen, da das Lauschen auf allen Schnittstellen unter Umständen nicht zuverlässig funktioniert.",
10
- "_manualIpAddresses": "Manuelle IP-Adressen",
11
- "_manualIpHint": "Hier können Sie IP-Adressen für Geräte eintragen, die nicht automatisch gefunden wurden.",
12
- "_discoveredDevicesHint": "Alle gefundenen Geräte (automatisch und manuell) werden unten angezeigt:",
13
- "_discoveredDevices": "Gefundene Geräte",
14
- "_disableAutoDiscovery": "Automatische Suche deaktivieren",
15
- "datapoint.model": "Spezifisches Modell der Lampe",
16
- "datapoint.ip": "IP-Adresse der Lampe",
17
- "datapoint.bleVersionHard": "Bluetooth Low Energy Hardware-Version",
18
- "datapoint.bleVersionSoft": "Bluetooth Low Energy Software-Version",
19
- "datapoint.wifiVersionHard": "WiFi-Hardware-Version",
20
- "datapoint.wifiVersionSoft": "WiFi-Software-Version"
2
+ "_configDescription": "Hier muss nichts konfiguriert werden. Bitte die Intervalle nur ändern, wenn dies explizit gewollt ist.",
3
+ "_deviceStatusRefreshInterval": "Aktualisierungsintervall für den Gerätestatus",
4
+ "_disableAutoDiscovery": "Automatische Suche deaktivieren",
5
+ "_discoveredDevices": "Gefundene Geräte",
6
+ "_discoveredDevicesHint": "Alle gefundenen Geräte (automatisch und manuell) werden unten angezeigt:",
7
+ "_extendedLogging": "Erweiterte Protokollierung aktiviert",
8
+ "_extendedLoggingDescription": "Die erweiterte Protokollierung druckt alle vom Gerät empfangenen Daten mit Ebeneninformationen in das Protokoll. Da dies zu einem großen Log-Spam führt, wird empfohlen, die Funktion nur bei Bedarf einzuschalten und anschließend wieder auszuschalten.",
9
+ "_manualIpAddresses": "Manuelle IP-Adressen",
10
+ "_manualIpHint": "Hier können Sie IP-Adressen für Geräte eintragen, die nicht automatisch gefunden wurden.",
11
+ "_networkHint": "Windows-Nutzer: Sie müssen eventuell Ihre IP-Adresse manuell auswählen, da das Lauschen auf allen Schnittstellen unter Umständen nicht zuverlässig funktioniert.",
12
+ "_networkInterface": "Netzwerkschnittstelle",
13
+ "_scanMode": "Geräteerkennungsmodus",
14
+ "_scanModeInterval": "Scannen im Intervall",
15
+ "_scanModeNever": "Scannen Sie niemals",
16
+ "_scanModeOnce": "Beim Start einmal scannen",
17
+ "_searchInterval": "Suchintervall",
18
+ "datapoint.bleVersionHard": "Bluetooth Low Energy Hardware-Version",
19
+ "datapoint.bleVersionSoft": "Bluetooth Low Energy Software-Version",
20
+ "datapoint.ip": "IP-Adresse der Lampe",
21
+ "datapoint.model": "Spezifisches Modell der Lampe",
22
+ "datapoint.wifiVersionHard": "WiFi-Hardware-Version",
23
+ "datapoint.wifiVersionSoft": "WiFi-Software-Version",
24
+ "govee-local adapter settings": "Adaptereinstellungen für govee-local",
25
+ "searchIntervalHint": "Scan-Intervall in Sekunden (nur relevant, wenn der Modus „Scan im Intervall“ ist)"
21
26
  }
@@ -12,6 +12,11 @@
12
12
  "_discoveredDevicesHint": "All discovered devices (auto and manual) are shown below:",
13
13
  "_discoveredDevices": "Discovered Devices",
14
14
  "_disableAutoDiscovery": "Disable auto-discovery",
15
+ "_scanMode": "Device discovery mode",
16
+ "_scanModeInterval": "Scan in interval",
17
+ "_scanModeOnce": "Scan once at startup",
18
+ "_scanModeNever": "Never scan",
19
+ "searchIntervalHint": "Scan interval in seconds (only relevant if mode is 'Scan in interval')",
15
20
  "datapoint.model": "Specific model of the Lamp",
16
21
  "datapoint.ip": "IP address of the Lamp",
17
22
  "datapoint.bleVersionHard": "Bluetooth Low Energy Hardware Version",
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Ajustes del adaptador para govee-local",
3
- "_searchInterval": "Intervalo de búsqueda",
4
- "_deviceStatusRefreshInterval": "intervalo de actualización del estado del dispositivo",
5
- "_configDescription": "No hay nada obligatorio para configurar. Cambie los intervalos solo cuando esté seguro de que esto es lo que necesita",
6
- "_extendedLoggingDescription": "El registro extendido imprime todos los datos recibidos por el dispositivo en el registro con información de nivel. Como esto produce un enorme spam de registros, se recomienda activarlo sólo cuando sea necesario y desactivarlo nuevamente después.",
7
- "_extendedLogging": "Registro extendido habilitado",
8
- "_networkInterface": "Interfaz de red",
9
- "datapoint.model": "Modelo específico de la Lámpara",
10
- "datapoint.ip": "Dirección IP de la lámpara",
11
- "datapoint.bleVersionHard": "Versión de hardware Bluetooth de bajo consumo",
12
- "datapoint.bleVersionSoft": "Versión del software Bluetooth de bajo consumo de energía",
13
- "datapoint.wifiVersionHard": "Versión de hardware WiFi",
14
- "datapoint.wifiVersionSoft": "Versión del software WiFi",
15
- "_networkHint": "Usuarios de Windows: es posible que deban seleccionar su dirección IP manualmente, ya que escuchar en todas las interfaces puede no funcionar de manera confiable.",
16
- "_manualIpAddresses": "Direcciones IP manuales",
17
- "_manualIpHint": "Puede agregar direcciones IP aquí para dispositivos que no fueron descubiertos automáticamente.",
18
- "_disableAutoDiscovery": "Desactivar el descubrimiento automático",
19
- "_discoveredDevicesHint": "Todos los dispositivos descubiertos (automáticos y manuales) se pueden encontrar en Objetos → govee-local.0 → información → dispositivos descubiertos",
20
- "_discoveredDevices": "Dispositivos descubiertos"
2
+ "_configDescription": "No hay nada obligatorio para configurar. Cambie los intervalos solo cuando esté seguro de que esto es lo que necesita",
3
+ "_deviceStatusRefreshInterval": "intervalo de actualización del estado del dispositivo",
4
+ "_disableAutoDiscovery": "Desactivar el descubrimiento automático",
5
+ "_discoveredDevices": "Dispositivos descubiertos",
6
+ "_discoveredDevicesHint": "Todos los dispositivos descubiertos (automáticos y manuales) se pueden encontrar en Objetos → govee-local.0 → información → dispositivos descubiertos",
7
+ "_extendedLogging": "Registro extendido habilitado",
8
+ "_extendedLoggingDescription": "El registro extendido imprime todos los datos recibidos por el dispositivo en el registro con información de nivel. Como esto produce un enorme spam de registros, se recomienda activarlo sólo cuando sea necesario y desactivarlo nuevamente después.",
9
+ "_manualIpAddresses": "Direcciones IP manuales",
10
+ "_manualIpHint": "Puede agregar direcciones IP aquí para dispositivos que no fueron descubiertos automáticamente.",
11
+ "_networkHint": "Usuarios de Windows: es posible que deban seleccionar su dirección IP manualmente, ya que escuchar en todas las interfaces puede no funcionar de manera confiable.",
12
+ "_networkInterface": "Interfaz de red",
13
+ "_scanMode": "Modo de descubrimiento de dispositivos",
14
+ "_scanModeInterval": "Escanear en intervalo",
15
+ "_scanModeNever": "Nunca escanear",
16
+ "_scanModeOnce": "Escanear una vez al inicio",
17
+ "_searchInterval": "Intervalo de búsqueda",
18
+ "datapoint.bleVersionHard": "Versión de hardware Bluetooth de bajo consumo",
19
+ "datapoint.bleVersionSoft": "Versión del software Bluetooth de bajo consumo de energía",
20
+ "datapoint.ip": "Dirección IP de la lámpara",
21
+ "datapoint.model": "Modelo específico de la Lámpara",
22
+ "datapoint.wifiVersionHard": "Versión de hardware WiFi",
23
+ "datapoint.wifiVersionSoft": "Versión del software WiFi",
24
+ "govee-local adapter settings": "Ajustes del adaptador para govee-local",
25
+ "searchIntervalHint": "Intervalo de escaneo en segundos (solo relevante si el modo es 'Escanear en intervalo')"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Paramètres d'adaptateur pour govee-local",
3
- "_searchInterval": "Intervalle de recherche",
4
- "_deviceStatusRefreshInterval": "intervalle d'actualisation de l'état de l'appareil",
5
- "_configDescription": "Il n'y a rien d'obligatoire à configurer. Veuillez modifier les intervalles uniquement lorsque vous êtes sûr que c'est ce dont vous avez besoin",
6
- "_extendedLoggingDescription": "La journalisation étendue imprime toutes les données reçues par l'appareil dans le journal avec les informations de niveau. Comme cela produit un énorme spam de journaux, il est recommandé de l'activer uniquement lorsque cela est nécessaire et de le désactiver à nouveau par la suite.",
7
- "_extendedLogging": "Journalisation étendue activée",
8
- "_networkInterface": "Interface réseau",
9
- "datapoint.model": "Modèle spécifique de la Lampe",
10
- "datapoint.ip": "Adresse IP de la lampe",
11
- "datapoint.bleVersionHard": "Version matérielle Bluetooth basse consommation",
12
- "datapoint.bleVersionSoft": "Version du logiciel Bluetooth basse consommation",
13
- "datapoint.wifiVersionHard": "Version du matériel Wi-Fi",
14
- "datapoint.wifiVersionSoft": "Version du logiciel Wi-Fi",
15
- "_networkHint": "Utilisateurs Windows : vous devrez peut-être sélectionner votre adresse IP manuellement, car l'écoute sur toutes les interfaces peut ne pas fonctionner de manière fiable.",
16
- "_manualIpAddresses": "Adresses IP manuelles",
17
- "_manualIpHint": "Vous pouvez ajouter ici des adresses IP pour les appareils qui n'ont pas été découverts automatiquement.",
18
- "_disableAutoDiscovery": "Désactiver la découverte automatique",
19
- "_discoveredDevicesHint": "Tous les appareils découverts (auto et manuels) peuvent être trouvés dans Objets → govee-local.0 → info → discoveredDevices",
20
- "_discoveredDevices": "Appareils découverts"
2
+ "_configDescription": "Il n'y a rien d'obligatoire à configurer. Veuillez modifier les intervalles uniquement lorsque vous êtes sûr que c'est ce dont vous avez besoin",
3
+ "_deviceStatusRefreshInterval": "intervalle d'actualisation de l'état de l'appareil",
4
+ "_disableAutoDiscovery": "Désactiver la découverte automatique",
5
+ "_discoveredDevices": "Appareils découverts",
6
+ "_discoveredDevicesHint": "Tous les appareils découverts (auto et manuels) peuvent être trouvés dans Objets → govee-local.0 → info → discoveredDevices",
7
+ "_extendedLogging": "Journalisation étendue activée",
8
+ "_extendedLoggingDescription": "La journalisation étendue imprime toutes les données reçues par l'appareil dans le journal avec les informations de niveau. Comme cela produit un énorme spam de journaux, il est recommandé de l'activer uniquement lorsque cela est nécessaire et de le désactiver à nouveau par la suite.",
9
+ "_manualIpAddresses": "Adresses IP manuelles",
10
+ "_manualIpHint": "Vous pouvez ajouter ici des adresses IP pour les appareils qui n'ont pas été découverts automatiquement.",
11
+ "_networkHint": "Utilisateurs Windows : vous devrez peut-être sélectionner votre adresse IP manuellement, car l'écoute sur toutes les interfaces peut ne pas fonctionner de manière fiable.",
12
+ "_networkInterface": "Interface réseau",
13
+ "_scanMode": "Mode découverte des appareils",
14
+ "_scanModeInterval": "Scan à intervalles",
15
+ "_scanModeNever": "Ne jamais scanner",
16
+ "_scanModeOnce": "Scanner une fois au démarrage",
17
+ "_searchInterval": "Intervalle de recherche",
18
+ "datapoint.bleVersionHard": "Version matérielle Bluetooth basse consommation",
19
+ "datapoint.bleVersionSoft": "Version du logiciel Bluetooth basse consommation",
20
+ "datapoint.ip": "Adresse IP de la lampe",
21
+ "datapoint.model": "Modèle spécifique de la Lampe",
22
+ "datapoint.wifiVersionHard": "Version du matériel Wi-Fi",
23
+ "datapoint.wifiVersionSoft": "Version du logiciel Wi-Fi",
24
+ "govee-local adapter settings": "Paramètres d'adaptateur pour govee-local",
25
+ "searchIntervalHint": "Intervalle de scan en secondes (uniquement pertinent si le mode est « Scan in interval »)"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Impostazioni dell'adattatore per govee-local",
3
- "_searchInterval": "Intervallo di ricerca",
4
- "_deviceStatusRefreshInterval": "intervallo di aggiornamento dello stato del dispositivo",
5
- "_configDescription": "Non c'è nulla di obbligatorio da configurare. Si prega di modificare gli intervalli solo quando si è sicuri che questo è ciò di cui si ha bisogno",
6
- "_extendedLoggingDescription": "La registrazione estesa stampa tutti i dati ricevuti dal dispositivo nel registro con le informazioni sul livello. Poiché ciò produce un enorme spam nei registri, si consiglia di attivarlo solo quando necessario e di disattivarlo nuovamente in seguito.",
7
- "_extendedLogging": "Registrazione estesa abilitata",
8
- "_networkInterface": "Interfaccia di rete",
9
- "datapoint.model": "Modello specifico della Lampada",
10
- "datapoint.ip": "Indirizzo IP della Lampada",
11
- "datapoint.bleVersionHard": "Versione hardware Bluetooth a basso consumo energetico",
12
- "datapoint.bleVersionSoft": "Versione del software Bluetooth a basso consumo energetico",
13
- "datapoint.wifiVersionHard": "Versione hardware Wi-Fi",
14
- "datapoint.wifiVersionSoft": "Versione del software Wi-Fi",
15
- "_networkHint": "Utenti Windows: potrebbe essere necessario selezionare manualmente il proprio indirizzo IP, poiché l'ascolto su tutte le interfacce potrebbe non funzionare in modo affidabile.",
16
- "_manualIpAddresses": "Indirizzi IP manuali",
17
- "_manualIpHint": "Puoi aggiungere qui gli indirizzi IP per i dispositivi che non sono stati rilevati automaticamente.",
18
- "_disableAutoDiscovery": "Disabilita il rilevamento automatico",
19
- "_discoveredDevicesHint": "Tutti i dispositivi rilevati (auto e manuale) possono essere trovati in Oggetti → govee-local.0 → informazioni → discoveredDevices",
20
- "_discoveredDevices": "Dispositivi rilevati"
2
+ "_configDescription": "Non c'è nulla di obbligatorio da configurare. Si prega di modificare gli intervalli solo quando si è sicuri che questo è ciò di cui si ha bisogno",
3
+ "_deviceStatusRefreshInterval": "intervallo di aggiornamento dello stato del dispositivo",
4
+ "_disableAutoDiscovery": "Disabilita il rilevamento automatico",
5
+ "_discoveredDevices": "Dispositivi rilevati",
6
+ "_discoveredDevicesHint": "Tutti i dispositivi rilevati (auto e manuale) possono essere trovati in Oggetti → govee-local.0 → informazioni → discoveredDevices",
7
+ "_extendedLogging": "Registrazione estesa abilitata",
8
+ "_extendedLoggingDescription": "La registrazione estesa stampa tutti i dati ricevuti dal dispositivo nel registro con le informazioni sul livello. Poiché ciò produce un enorme spam nei registri, si consiglia di attivarlo solo quando necessario e di disattivarlo nuovamente in seguito.",
9
+ "_manualIpAddresses": "Indirizzi IP manuali",
10
+ "_manualIpHint": "Puoi aggiungere qui gli indirizzi IP per i dispositivi che non sono stati rilevati automaticamente.",
11
+ "_networkHint": "Utenti Windows: potrebbe essere necessario selezionare manualmente il proprio indirizzo IP, poiché l'ascolto su tutte le interfacce potrebbe non funzionare in modo affidabile.",
12
+ "_networkInterface": "Interfaccia di rete",
13
+ "_scanMode": "Modalità di rilevamento del dispositivo",
14
+ "_scanModeInterval": "Scansione a intervalli",
15
+ "_scanModeNever": "Non eseguire mai la scansione",
16
+ "_scanModeOnce": "Esegui la scansione una volta all'avvio",
17
+ "_searchInterval": "Intervallo di ricerca",
18
+ "datapoint.bleVersionHard": "Versione hardware Bluetooth a basso consumo energetico",
19
+ "datapoint.bleVersionSoft": "Versione del software Bluetooth a basso consumo energetico",
20
+ "datapoint.ip": "Indirizzo IP della Lampada",
21
+ "datapoint.model": "Modello specifico della Lampada",
22
+ "datapoint.wifiVersionHard": "Versione hardware Wi-Fi",
23
+ "datapoint.wifiVersionSoft": "Versione del software Wi-Fi",
24
+ "govee-local adapter settings": "Impostazioni dell'adattatore per govee-local",
25
+ "searchIntervalHint": "Intervallo di scansione in secondi (rilevante solo se la modalità è \"Scansione in intervallo\")"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Adapterinstellingen voor govee-local",
3
- "_searchInterval": "Zoek interval",
4
- "_deviceStatusRefreshInterval": "vernieuwingsinterval apparaatstatus",
5
- "_configDescription": "Er is niets verplicht om te configureren. Wijzig de intervallen alleen als u zeker weet dat dit is wat u nodig heeft",
6
- "_extendedLoggingDescription": "Uitgebreide logboekregistratie drukt alle door het apparaat ontvangen gegevens af naar het logboek met niveau-informatie. Omdat dit een enorme log-spam veroorzaakt, is het aan te raden dit alleen in te schakelen als dat nodig is en daarna weer uit te schakelen.",
7
- "_extendedLogging": "Uitgebreide logboekregistratie ingeschakeld",
8
- "_networkInterface": "Netwerkinterface",
9
- "datapoint.model": "Specifiek model van de lamp",
10
- "datapoint.ip": "IP-adres van de lamp",
11
- "datapoint.bleVersionHard": "Bluetooth Low Energy-hardwareversie",
12
- "datapoint.bleVersionSoft": "Bluetooth Low Energy-softwareversie",
13
- "datapoint.wifiVersionHard": "WiFi-hardwareversie",
14
- "datapoint.wifiVersionSoft": "WiFi-softwareversie",
15
- "_networkHint": "Windows-gebruikers: Mogelijk moet u uw IP-adres handmatig selecteren, omdat het luisteren op alle interfaces mogelijk niet betrouwbaar werkt.",
16
- "_manualIpAddresses": "Handmatige IP-adressen",
17
- "_manualIpHint": "U kunt hier IP-adressen toevoegen voor apparaten die niet automatisch zijn ontdekt.",
18
- "_disableAutoDiscovery": "Schakel automatische detectie uit",
19
- "_discoveredDevicesHint": "Alle ontdekte apparaten (automatisch en handmatig) zijn te vinden in Objecten → govee-local.0 → info → ontdekte apparaten",
20
- "_discoveredDevices": "Ontdekte apparaten"
2
+ "_configDescription": "Er is niets verplicht om te configureren. Wijzig de intervallen alleen als u zeker weet dat dit is wat u nodig heeft",
3
+ "_deviceStatusRefreshInterval": "vernieuwingsinterval apparaatstatus",
4
+ "_disableAutoDiscovery": "Schakel automatische detectie uit",
5
+ "_discoveredDevices": "Ontdekte apparaten",
6
+ "_discoveredDevicesHint": "Alle ontdekte apparaten (automatisch en handmatig) zijn te vinden in Objecten → govee-local.0 → info → ontdekte apparaten",
7
+ "_extendedLogging": "Uitgebreide logboekregistratie ingeschakeld",
8
+ "_extendedLoggingDescription": "Uitgebreide logboekregistratie drukt alle door het apparaat ontvangen gegevens af naar het logboek met niveau-informatie. Omdat dit een enorme log-spam veroorzaakt, is het aan te raden dit alleen in te schakelen als dat nodig is en daarna weer uit te schakelen.",
9
+ "_manualIpAddresses": "Handmatige IP-adressen",
10
+ "_manualIpHint": "U kunt hier IP-adressen toevoegen voor apparaten die niet automatisch zijn ontdekt.",
11
+ "_networkHint": "Windows-gebruikers: Mogelijk moet u uw IP-adres handmatig selecteren, omdat het luisteren op alle interfaces mogelijk niet betrouwbaar werkt.",
12
+ "_networkInterface": "Netwerkinterface",
13
+ "_scanMode": "Apparaatdetectiemodus",
14
+ "_scanModeInterval": "Scannen in interval",
15
+ "_scanModeNever": "Nooit scannen",
16
+ "_scanModeOnce": "Eén keer scannen bij het opstarten",
17
+ "_searchInterval": "Zoek interval",
18
+ "datapoint.bleVersionHard": "Bluetooth Low Energy-hardwareversie",
19
+ "datapoint.bleVersionSoft": "Bluetooth Low Energy-softwareversie",
20
+ "datapoint.ip": "IP-adres van de lamp",
21
+ "datapoint.model": "Specifiek model van de lamp",
22
+ "datapoint.wifiVersionHard": "WiFi-hardwareversie",
23
+ "datapoint.wifiVersionSoft": "WiFi-softwareversie",
24
+ "govee-local adapter settings": "Adapterinstellingen voor govee-local",
25
+ "searchIntervalHint": "Scaninterval in seconden (alleen relevant als de modus 'Scan in interval' is)"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Ustawienia adaptera dla govee-local",
3
- "_searchInterval": "Interwał wyszukiwania",
4
- "_deviceStatusRefreshInterval": "interwał odświeżania stanu urządzenia",
5
- "_configDescription": "Nie ma nic obowiązkowego do skonfigurowania. Zmień interwały tylko wtedy, gdy jesteś pewien, że tego właśnie potrzebujesz",
6
- "_extendedLoggingDescription": "Rozszerzone rejestrowanie drukuje wszystkie dane otrzymane przez urządzenie w dzienniku z informacjami o poziomie. Ponieważ powoduje to powstawanie ogromnego spamu w dziennikach, zaleca się włączenie tej opcji tylko w razie potrzeby, a następnie wyłączenie jej ponownie.",
7
- "_extendedLogging": "Włączono rozszerzone rejestrowanie",
8
- "_networkInterface": "Interfejs sieciowy",
9
- "datapoint.model": "Konkretny model lampy",
10
- "datapoint.ip": "Adres IP Lampy",
11
- "datapoint.bleVersionHard": "Wersja sprzętu Bluetooth o niskim zużyciu energii",
12
- "datapoint.bleVersionSoft": "Wersja oprogramowania Bluetooth Low Energy",
13
- "datapoint.wifiVersionHard": "Wersja sprzętu Wi-Fi",
14
- "datapoint.wifiVersionSoft": "Wersja oprogramowania Wi-Fi",
15
- "_networkHint": "Użytkownicy systemu Windows: Może być konieczne ręczne wybranie adresu IP, ponieważ nasłuchiwanie na wszystkich interfejsach może nie działać niezawodnie.",
16
- "_manualIpAddresses": "Ręczne adresy IP",
17
- "_manualIpHint": "Możesz tutaj dodać adresy IP dla urządzeń, które nie zostały automatycznie wykryte.",
18
- "_disableAutoDiscovery": "Wyłącz automatyczne wykrywanie",
19
- "_discoveredDevicesHint": "Wszystkie wykryte urządzenia (automatyczne i ręczne) można znaleźć w Obiekty → govee-local.0 → info → discoveredDevices",
20
- "_discoveredDevices": "Odkryte urządzenia"
2
+ "_configDescription": "Nie ma nic obowiązkowego do skonfigurowania. Zmień interwały tylko wtedy, gdy jesteś pewien, że tego właśnie potrzebujesz",
3
+ "_deviceStatusRefreshInterval": "interwał odświeżania stanu urządzenia",
4
+ "_disableAutoDiscovery": "Wyłącz automatyczne wykrywanie",
5
+ "_discoveredDevices": "Odkryte urządzenia",
6
+ "_discoveredDevicesHint": "Wszystkie wykryte urządzenia (automatyczne i ręczne) można znaleźć w Obiekty → govee-local.0 → info → discoveredDevices",
7
+ "_extendedLogging": "Włączono rozszerzone rejestrowanie",
8
+ "_extendedLoggingDescription": "Rozszerzone rejestrowanie drukuje wszystkie dane otrzymane przez urządzenie w dzienniku z informacjami o poziomie. Ponieważ powoduje to powstawanie ogromnego spamu w dziennikach, zaleca się włączenie tej opcji tylko w razie potrzeby, a następnie wyłączenie jej ponownie.",
9
+ "_manualIpAddresses": "Ręczne adresy IP",
10
+ "_manualIpHint": "Możesz tutaj dodać adresy IP dla urządzeń, które nie zostały automatycznie wykryte.",
11
+ "_networkHint": "Użytkownicy systemu Windows: Może być konieczne ręczne wybranie adresu IP, ponieważ nasłuchiwanie na wszystkich interfejsach może nie działać niezawodnie.",
12
+ "_networkInterface": "Interfejs sieciowy",
13
+ "_scanMode": "Tryb wykrywania urządzenia",
14
+ "_scanModeInterval": "Skanuj w odstępie czasu",
15
+ "_scanModeNever": "Nigdy nie skanuj",
16
+ "_scanModeOnce": "Skanuj raz przy uruchomieniu",
17
+ "_searchInterval": "Interwał wyszukiwania",
18
+ "datapoint.bleVersionHard": "Wersja sprzętu Bluetooth o niskim zużyciu energii",
19
+ "datapoint.bleVersionSoft": "Wersja oprogramowania Bluetooth Low Energy",
20
+ "datapoint.ip": "Adres IP Lampy",
21
+ "datapoint.model": "Konkretny model lampy",
22
+ "datapoint.wifiVersionHard": "Wersja sprzętu Wi-Fi",
23
+ "datapoint.wifiVersionSoft": "Wersja oprogramowania Wi-Fi",
24
+ "govee-local adapter settings": "Ustawienia adaptera dla govee-local",
25
+ "searchIntervalHint": "Interwał skanowania w sekundach (istotne tylko w przypadku trybu „Skanuj w odstępie”)"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Configurações do adaptador para govee-local",
3
- "_searchInterval": "Intervalo de pesquisa",
4
- "_deviceStatusRefreshInterval": "intervalo de atualização do status do dispositivo",
5
- "_configDescription": "Não há nada obrigatório para configurar. Por favor, altere os intervalos somente quando tiver certeza de que é isso que você precisa",
6
- "_extendedLoggingDescription": "O Extended Logging imprime todos os dados recebidos pelo dispositivo no log com informações de nível. Como isso produz um grande spam de log, é recomendável ativá-lo somente quando necessário e desligá-lo novamente depois.",
7
- "_extendedLogging": "Registro estendido ativado",
8
- "_networkInterface": "Interface de rede",
9
- "datapoint.model": "Modelo específico da Lâmpada",
10
- "datapoint.ip": "Endereço IP da lâmpada",
11
- "datapoint.bleVersionHard": "Versão de hardware Bluetooth de baixo consumo de energia",
12
- "datapoint.bleVersionSoft": "Versão do software Bluetooth de baixo consumo de energia",
13
- "datapoint.wifiVersionHard": "Versão de hardware WiFi",
14
- "datapoint.wifiVersionSoft": "Versão do software Wi-Fi",
15
- "_networkHint": "Usuários do Windows: talvez seja necessário selecionar seu endereço IP manualmente, pois a escuta em todas as interfaces pode não funcionar de maneira confiável.",
16
- "_manualIpAddresses": "Endereços IP manuais",
17
- "_manualIpHint": "Você pode adicionar endereços IP aqui para dispositivos que não foram descobertos automaticamente.",
18
- "_disableAutoDiscovery": "Desativar descoberta automática",
19
- "_discoveredDevicesHint": "Todos os dispositivos descobertos (automáticos e manuais) podem ser encontrados em Objetos → govee-local.0 → informações → dispositivos descobertos",
20
- "_discoveredDevices": "Dispositivos descobertos"
2
+ "_configDescription": "Não há nada obrigatório para configurar. Por favor, altere os intervalos somente quando tiver certeza de que é isso que você precisa",
3
+ "_deviceStatusRefreshInterval": "intervalo de atualização do status do dispositivo",
4
+ "_disableAutoDiscovery": "Desativar descoberta automática",
5
+ "_discoveredDevices": "Dispositivos descobertos",
6
+ "_discoveredDevicesHint": "Todos os dispositivos descobertos (automáticos e manuais) podem ser encontrados em Objetos → govee-local.0 → informações → dispositivos descobertos",
7
+ "_extendedLogging": "Registro estendido ativado",
8
+ "_extendedLoggingDescription": "O Extended Logging imprime todos os dados recebidos pelo dispositivo no log com informações de nível. Como isso produz um grande spam de log, é recomendável ativá-lo somente quando necessário e desligá-lo novamente depois.",
9
+ "_manualIpAddresses": "Endereços IP manuais",
10
+ "_manualIpHint": "Você pode adicionar endereços IP aqui para dispositivos que não foram descobertos automaticamente.",
11
+ "_networkHint": "Usuários do Windows: talvez seja necessário selecionar seu endereço IP manualmente, pois a escuta em todas as interfaces pode não funcionar de maneira confiável.",
12
+ "_networkInterface": "Interface de rede",
13
+ "_scanMode": "Modo de descoberta de dispositivos",
14
+ "_scanModeInterval": "Digitalizar em intervalo",
15
+ "_scanModeNever": "Nunca digitalize",
16
+ "_scanModeOnce": "Digitalize uma vez na inicialização",
17
+ "_searchInterval": "Intervalo de pesquisa",
18
+ "datapoint.bleVersionHard": "Versão de hardware Bluetooth de baixo consumo de energia",
19
+ "datapoint.bleVersionSoft": "Versão do software Bluetooth de baixo consumo de energia",
20
+ "datapoint.ip": "Endereço IP da lâmpada",
21
+ "datapoint.model": "Modelo específico da Lâmpada",
22
+ "datapoint.wifiVersionHard": "Versão de hardware WiFi",
23
+ "datapoint.wifiVersionSoft": "Versão do software Wi-Fi",
24
+ "govee-local adapter settings": "Configurações do adaptador para govee-local",
25
+ "searchIntervalHint": "Intervalo de varredura em segundos (relevante apenas se o modo for 'Scan in interval')"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Настройки адаптера для govee-local",
3
- "_searchInterval": "Интервал поиска",
4
- "_deviceStatusRefreshInterval": "интервал обновления статуса устройства",
5
- "_configDescription": "Нет ничего обязательного для настройки. Пожалуйста, меняйте интервалы только тогда, когда вы уверены, что это то, что вам нужно.",
6
- "_extendedLoggingDescription": "Расширенная регистрация записывает все данные, полученные устройством, в журнал с информацией об уровне. Поскольку это приводит к большому количеству спама в журналах, рекомендуется включать эту функцию только при необходимости, а затем снова выключать.",
7
- "_extendedLogging": "Расширенное ведение журнала включено",
8
- "_networkInterface": "Сетевой интерфейс",
9
- "datapoint.model": "Конкретная модель лампы",
10
- "datapoint.ip": "IP-адрес лампы",
11
- "datapoint.bleVersionHard": "Аппаратная версия Bluetooth с низким энергопотреблением",
12
- "datapoint.bleVersionSoft": "Версия программного обеспечения Bluetooth с низким энергопотреблением",
13
- "datapoint.wifiVersionHard": "Версия оборудования Wi-Fi",
14
- "datapoint.wifiVersionSoft": "Версия программного обеспечения Wi-Fi",
15
- "_networkHint": "Пользователи Windows: возможно, вам придется выбрать IP-адрес вручную, поскольку прослушивание всех интерфейсов может работать ненадежно.",
16
- "_manualIpAddresses": "IP-адреса вручную",
17
- "_manualIpHint": "Здесь вы можете добавить IP-адреса для устройств, которые не были обнаружены автоматически.",
18
- "_disableAutoDiscovery": "Отключить автоматическое обнаружение",
19
- "_discoveredDevicesHint": "Все обнаруженные устройства (автоматические и ручные) можно найти в Objects → govee-local.0 → info → discoveredDevices.",
20
- "_discoveredDevices": "Обнаруженные устройства"
2
+ "_configDescription": "Нет ничего обязательного для настройки. Пожалуйста, меняйте интервалы только тогда, когда вы уверены, что это то, что вам нужно.",
3
+ "_deviceStatusRefreshInterval": "интервал обновления статуса устройства",
4
+ "_disableAutoDiscovery": "Отключить автоматическое обнаружение",
5
+ "_discoveredDevices": "Обнаруженные устройства",
6
+ "_discoveredDevicesHint": "Все обнаруженные устройства (автоматические и ручные) можно найти в Objects → govee-local.0 → info → discoveredDevices.",
7
+ "_extendedLogging": "Расширенное ведение журнала включено",
8
+ "_extendedLoggingDescription": "Расширенная регистрация записывает все данные, полученные устройством, в журнал с информацией об уровне. Поскольку это приводит к большому количеству спама в журналах, рекомендуется включать эту функцию только при необходимости, а затем снова выключать.",
9
+ "_manualIpAddresses": "IP-адреса вручную",
10
+ "_manualIpHint": "Здесь вы можете добавить IP-адреса для устройств, которые не были обнаружены автоматически.",
11
+ "_networkHint": "Пользователи Windows: возможно, вам придется выбрать IP-адрес вручную, поскольку прослушивание всех интерфейсов может работать ненадежно.",
12
+ "_networkInterface": "Сетевой интерфейс",
13
+ "_scanMode": "Режим обнаружения устройств",
14
+ "_scanModeInterval": "Сканировать через интервал",
15
+ "_scanModeNever": "Никогда не сканировать",
16
+ "_scanModeOnce": "Сканировать один раз при запуске",
17
+ "_searchInterval": "Интервал поиска",
18
+ "datapoint.bleVersionHard": "Аппаратная версия Bluetooth с низким энергопотреблением",
19
+ "datapoint.bleVersionSoft": "Версия программного обеспечения Bluetooth с низким энергопотреблением",
20
+ "datapoint.ip": "IP-адрес лампы",
21
+ "datapoint.model": "Конкретная модель лампы",
22
+ "datapoint.wifiVersionHard": "Версия оборудования Wi-Fi",
23
+ "datapoint.wifiVersionSoft": "Версия программного обеспечения Wi-Fi",
24
+ "govee-local adapter settings": "Настройки адаптера для govee-local",
25
+ "searchIntervalHint": "Интервал сканирования в секундах (актуально только в том случае, если выбран режим «Сканировать через интервал»)"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "Adapter settings for govee-local",
3
- "_searchInterval": "Інтервал пошуку",
4
- "_deviceStatusRefreshInterval": "інтервал оновлення стану пристрою",
5
- "_configDescription": "Немає нічого обов’язкового для налаштування. Будь ласка, змінюйте інтервали лише тоді, коли ви впевнені, що це те, що вам потрібно",
6
- "_extendedLoggingDescription": "Extended Logging друкує всі дані, отримані пристроєм, у журналі з інформацією про рівень. Оскільки це створює величезну кількість спаму в журналі, рекомендується вмикати це лише за потреби, а потім знову вимикати.",
7
- "_extendedLogging": "Розширене журналювання ввімкнено",
8
- "_networkInterface": "Мережевий інтерфейс",
9
- "datapoint.model": "Конкретна модель лампи",
10
- "datapoint.ip": "IP-адреса лампи",
11
- "datapoint.bleVersionHard": "Апаратна версія Bluetooth Low Energy",
12
- "datapoint.bleVersionSoft": "Версія програмного забезпечення Bluetooth Low Energy",
13
- "datapoint.wifiVersionHard": "Версія обладнання WiFi",
14
- "datapoint.wifiVersionSoft": "Версія програмного забезпечення WiFi",
15
- "_networkHint": "Користувачі Windows: можливо, вам доведеться вибрати свою IP-адресу вручну, оскільки прослуховування на всіх інтерфейсах може працювати ненадійно.",
16
- "_manualIpAddresses": "IP-адреси вручну",
17
- "_manualIpHint": "Тут можна додати IP-адреси для пристроїв, які не були виявлені автоматично.",
18
- "_disableAutoDiscovery": "Вимкнути автоматичне виявлення",
19
- "_discoveredDevicesHint": "Усі виявлені пристрої (автоматичні та ручні) можна знайти в Objects → govee-local.0 → info → discoveredDevices",
20
- "_discoveredDevices": "Виявлені пристрої"
2
+ "_configDescription": "Немає нічого обов’язкового для налаштування. Будь ласка, змінюйте інтервали лише тоді, коли ви впевнені, що це те, що вам потрібно",
3
+ "_deviceStatusRefreshInterval": "інтервал оновлення стану пристрою",
4
+ "_disableAutoDiscovery": "Вимкнути автоматичне виявлення",
5
+ "_discoveredDevices": "Виявлені пристрої",
6
+ "_discoveredDevicesHint": "Усі виявлені пристрої (автоматичні та ручні) можна знайти в Objects → govee-local.0 → info → discoveredDevices",
7
+ "_extendedLogging": "Розширене журналювання ввімкнено",
8
+ "_extendedLoggingDescription": "Extended Logging друкує всі дані, отримані пристроєм, у журналі з інформацією про рівень. Оскільки це створює величезну кількість спаму в журналі, рекомендується вмикати це лише за потреби, а потім знову вимикати.",
9
+ "_manualIpAddresses": "IP-адреси вручну",
10
+ "_manualIpHint": "Тут можна додати IP-адреси для пристроїв, які не були виявлені автоматично.",
11
+ "_networkHint": "Користувачі Windows: можливо, вам доведеться вибрати свою IP-адресу вручну, оскільки прослуховування на всіх інтерфейсах може працювати ненадійно.",
12
+ "_networkInterface": "Мережевий інтерфейс",
13
+ "_scanMode": "Режим виявлення пристрою",
14
+ "_scanModeInterval": "Сканування з інтервалом",
15
+ "_scanModeNever": "Ніколи не скануйте",
16
+ "_scanModeOnce": "Сканувати один раз під час запуску",
17
+ "_searchInterval": "Інтервал пошуку",
18
+ "datapoint.bleVersionHard": "Апаратна версія Bluetooth Low Energy",
19
+ "datapoint.bleVersionSoft": "Версія програмного забезпечення Bluetooth Low Energy",
20
+ "datapoint.ip": "IP-адреса лампи",
21
+ "datapoint.model": "Конкретна модель лампи",
22
+ "datapoint.wifiVersionHard": "Версія обладнання WiFi",
23
+ "datapoint.wifiVersionSoft": "Версія програмного забезпечення WiFi",
24
+ "govee-local adapter settings": "Adapter settings for govee-local",
25
+ "searchIntervalHint": "Інтервал сканування в секундах (релевантно, лише якщо режим «Інтервальне сканування»)"
21
26
  }
@@ -1,21 +1,26 @@
1
1
  {
2
- "govee-local adapter settings": "govee-local的适配器设置",
3
- "_searchInterval": "搜索间隔",
4
- "_deviceStatusRefreshInterval": "设备状态刷新间隔",
5
- "_configDescription": "没有什么必须配置的。仅当您确定这是您需要的时才更改间隔",
6
- "_extendedLoggingDescription": "扩展日志记录将设备接收到的所有数据打印到带有级别信息的日志中。由于这会产生大量日志垃圾邮件,因此建议仅在需要时打开此功能,然后再次关闭。",
7
- "_extendedLogging": "启用扩展日志记录",
8
- "_networkInterface": "网络接口",
9
- "datapoint.model": "灯的具体型号",
10
- "datapoint.ip": "灯的IP地址",
11
- "datapoint.bleVersionHard": "蓝牙低功耗硬件版本",
12
- "datapoint.bleVersionSoft": "低功耗蓝牙软件版本",
13
- "datapoint.wifiVersionHard": "WiFi硬件版本",
14
- "datapoint.wifiVersionSoft": "WiFi软件版本",
15
- "_networkHint": "Windows 用户:您可能需要手动选择 IP 地址,因为侦听所有接口可能无法可靠地工作。",
16
- "_manualIpAddresses": "手动 IP 地址",
17
- "_manualIpHint": "您可以在此处为未自动发现的设备添加 IP 地址。",
18
- "_disableAutoDiscovery": "禁用自动发现",
19
- "_discoveredDevicesHint": "所有发现的设备(自动和手动)都可以在对象 → govee-local.0 → 信息 →discoveredDevices 中找到",
20
- "_discoveredDevices": "发现的设备"
2
+ "_configDescription": "没有什么必须配置的。仅当您确定这是您需要的时才更改间隔",
3
+ "_deviceStatusRefreshInterval": "设备状态刷新间隔",
4
+ "_disableAutoDiscovery": "禁用自动发现",
5
+ "_discoveredDevices": "发现的设备",
6
+ "_discoveredDevicesHint": "所有发现的设备(自动和手动)都可以在对象 → govee-local.0 → 信息 →discoveredDevices 中找到",
7
+ "_extendedLogging": "启用扩展日志记录",
8
+ "_extendedLoggingDescription": "扩展日志记录将设备接收到的所有数据打印到带有级别信息的日志中。由于这会产生大量日志垃圾邮件,因此建议仅在需要时打开此功能,然后再次关闭。",
9
+ "_manualIpAddresses": "手动 IP 地址",
10
+ "_manualIpHint": "您可以在此处为未自动发现的设备添加 IP 地址。",
11
+ "_networkHint": "Windows 用户:您可能需要手动选择 IP 地址,因为侦听所有接口可能无法可靠地工作。",
12
+ "_networkInterface": "网络接口",
13
+ "_scanMode": "设备发现模式",
14
+ "_scanModeInterval": "间隔扫描",
15
+ "_scanModeNever": "从不扫描",
16
+ "_scanModeOnce": "启动时扫描一次",
17
+ "_searchInterval": "搜索间隔",
18
+ "datapoint.bleVersionHard": "蓝牙低功耗硬件版本",
19
+ "datapoint.bleVersionSoft": "低功耗蓝牙软件版本",
20
+ "datapoint.ip": "灯的IP地址",
21
+ "datapoint.model": "灯的具体型号",
22
+ "datapoint.wifiVersionHard": "WiFi硬件版本",
23
+ "datapoint.wifiVersionSoft": "WiFi软件版本",
24
+ "govee-local adapter settings": "govee-local的适配器设置",
25
+ "searchIntervalHint": "扫描间隔(以秒为单位)(仅在模式为“间隔扫描”时相关)"
21
26
  }
@@ -31,10 +31,25 @@
31
31
  },
32
32
  "intervalsHeader": {
33
33
  "type": "header",
34
- "text": "Intervals",
34
+ "text": "_scanMode",
35
35
  "size": 2,
36
36
  "xs": 12
37
37
  },
38
+ "scanMode": {
39
+ "type": "select",
40
+ "label": "_scanMode",
41
+ "options": [
42
+ { "value": "interval", "label": "_scanModeInterval" },
43
+ { "value": "once", "label": "_scanModeOnce" },
44
+ { "value": "never", "label": "_scanModeNever" }
45
+ ],
46
+ "default": "interval",
47
+ "xs": 12,
48
+ "sm": 12,
49
+ "md": 6,
50
+ "lg": 4,
51
+ "xl": 4
52
+ },
38
53
  "searchInterval": {
39
54
  "type": "number",
40
55
  "min": 1,
@@ -44,7 +59,7 @@
44
59
  "md": 3,
45
60
  "lg": 2,
46
61
  "xl": 2,
47
- "label": "_searchInterval"
62
+ "label": "_searchIntervalHint"
48
63
  },
49
64
  "deviceStatusRefreshInterval": {
50
65
  "type": "number",
@@ -105,12 +120,6 @@
105
120
  "lg": 12,
106
121
  "xl": 12
107
122
  },
108
- "disableAutoDiscovery": {
109
- "type": "checkbox",
110
- "label": "_disableAutoDiscovery",
111
- "default": false,
112
- "xs": 12
113
- },
114
123
  "loggingHeader": {
115
124
  "type": "header",
116
125
  "text": "Logging",
@@ -42,6 +42,7 @@ class GoveeService extends import_node_events.EventEmitter {
42
42
  loggedDevices = [];
43
43
  searchInterval;
44
44
  refreshInterval;
45
+ scanMode = "interval";
45
46
  static LOCAL_PORT = 4002;
46
47
  static SEND_SCAN_PORT = 4001;
47
48
  static CONTROL_PORT = 4003;
@@ -57,6 +58,11 @@ class GoveeService extends import_node_events.EventEmitter {
57
58
  super();
58
59
  this.options = options;
59
60
  this.socket = dgram.createSocket({ type: "udp4" });
61
+ if (options.scanMode === "once" || options.scanMode === "never") {
62
+ this.scanMode = options.scanMode;
63
+ } else {
64
+ this.scanMode = "interval";
65
+ }
60
66
  }
61
67
  /**
62
68
  * Bind UDP socket and start device search/refresh intervals.
@@ -73,7 +79,7 @@ class GoveeService extends import_node_events.EventEmitter {
73
79
  );
74
80
  }
75
81
  serverBound() {
76
- var _a, _b, _c, _d;
82
+ var _a, _b, _c, _d, _e;
77
83
  this.socket.setBroadcast(true);
78
84
  this.socket.setMulticastTTL(128);
79
85
  this.socket.setMulticastInterface(this.options.interface);
@@ -87,11 +93,14 @@ class GoveeService extends import_node_events.EventEmitter {
87
93
  `Added ${this.options.manualIpAddresses.length} manual device(s) from configuration.`
88
94
  );
89
95
  }
90
- if (!this.options.disableAutoDiscovery) {
96
+ if (this.scanMode === "interval") {
91
97
  this.searchInterval = setInterval(() => this.sendScan(), this.options.searchInterval * 1e3);
92
98
  (_c = this.options.logger) == null ? void 0 : _c.debug(`Auto-discovery started with interval ${this.options.searchInterval} seconds.`);
93
- } else {
94
- (_d = this.options.logger) == null ? void 0 : _d.debug("Auto-discovery is disabled. Only manual devices will be used.");
99
+ } else if (this.scanMode === "once") {
100
+ this.sendScan();
101
+ (_d = this.options.logger) == null ? void 0 : _d.debug("Auto-discovery: scan once at startup.");
102
+ } else if (this.scanMode === "never") {
103
+ (_e = this.options.logger) == null ? void 0 : _e.debug("Auto-discovery is disabled. Only manual devices will be used.");
95
104
  }
96
105
  this.refreshInterval = setInterval(
97
106
  () => this.refreshAllDevices(),
@@ -106,18 +115,26 @@ class GoveeService extends import_node_events.EventEmitter {
106
115
  * @param remote The sender info.
107
116
  */
108
117
  onUdpMessage(message, remote) {
109
- var _a, _b, _c, _d;
110
- const messageObject = JSON.parse(message.toString());
118
+ var _a, _b, _c, _d, _e;
119
+ let messageObject;
120
+ try {
121
+ messageObject = JSON.parse(message.toString());
122
+ } catch (err) {
123
+ (_a = this.options.logger) == null ? void 0 : _a.error(
124
+ `Malformed UDP message from ${remote.address}:${remote.port}: ${err instanceof Error ? err.message : String(err)}`
125
+ );
126
+ throw err;
127
+ }
111
128
  switch (messageObject.msg.cmd) {
112
129
  case "scan": {
113
130
  if (messageObject.msg.data.device) {
114
131
  const deviceName = messageObject.msg.data.device.replace(
115
- (_a = this.options.forbiddenChars) != null ? _a : /[^a-zA-Z0-9_-]/g,
132
+ (_b = this.options.forbiddenChars) != null ? _b : /[^a-zA-Z0-9_-]/g,
116
133
  "_"
117
134
  );
118
135
  this.devices[remote.address] = deviceName;
119
136
  if (this.options.extendedLogging) {
120
- (_b = this.options.logger) == null ? void 0 : _b.info(
137
+ (_c = this.options.logger) == null ? void 0 : _c.info(
121
138
  `Discovered device: ${deviceName} at ${remote.address} (model: ${messageObject.msg.data.sku})`
122
139
  );
123
140
  }
@@ -133,7 +150,7 @@ class GoveeService extends import_node_events.EventEmitter {
133
150
  const sendingDevice = this.devices[remote.address];
134
151
  if (sendingDevice) {
135
152
  if (this.options.extendedLogging && !this.loggedDevices.includes(remote.address.toString())) {
136
- (_c = this.options.logger) == null ? void 0 : _c.info(`device status message data: ${JSON.stringify(messageObject)}`);
153
+ (_d = this.options.logger) == null ? void 0 : _d.info(`device status message data: ${JSON.stringify(messageObject)}`);
137
154
  this.loggedDevices.push(remote.address.toString());
138
155
  }
139
156
  this.emitDeviceStatusUpdate(sendingDevice, remote.address, messageObject);
@@ -141,7 +158,7 @@ class GoveeService extends import_node_events.EventEmitter {
141
158
  break;
142
159
  }
143
160
  default: {
144
- (_d = this.options.logger) == null ? void 0 : _d.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);
161
+ (_e = this.options.logger) == null ? void 0 : _e.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);
145
162
  }
146
163
  }
147
164
  }
@@ -327,7 +344,13 @@ class GoveeService extends import_node_events.EventEmitter {
327
344
  */
328
345
  emitDeviceStatusUpdate(deviceName, ip, messageObject) {
329
346
  const deviceData = messageObject.msg.data;
330
- const colorString = `#${(0, import_hexTool.componentToHex)(deviceData.color.r)}${(0, import_hexTool.componentToHex)(deviceData.color.g)}${(0, import_hexTool.componentToHex)(deviceData.color.b)}`;
347
+ let colorString = "#000000";
348
+ if (deviceData.color && typeof deviceData.color === "object") {
349
+ const r = typeof deviceData.color.r === "number" ? deviceData.color.r : 0;
350
+ const g = typeof deviceData.color.g === "number" ? deviceData.color.g : 0;
351
+ const b = typeof deviceData.color.b === "number" ? deviceData.color.b : 0;
352
+ colorString = `#${(0, import_hexTool.componentToHex)(r)}${(0, import_hexTool.componentToHex)(g)}${(0, import_hexTool.componentToHex)(b)}`;
353
+ }
331
354
  this.emit("deviceStatusUpdate", {
332
355
  deviceName,
333
356
  ip,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/lib/goveeService.ts"],
4
- "sourcesContent": ["/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n */\n\nimport * as dgram from 'node:dgram';\nimport { EventEmitter } from 'node:events';\nimport type { GoveeServiceOptions } from './goveeServiceOptions';\nimport { componentToHex, hexToRgb } from './tools/hexTool';\nimport { isValidIpAddress } from './tools/ipValidation';\n\n/**\n * Device discovery event data.\n */\nexport interface DeviceDiscoveryEvent {\n\t/** The IP address of the discovered device. */\n\tip: string;\n\t/** The sanitized device name. */\n\tdeviceName: string;\n\t/** The device model. */\n\tdeviceModel: string;\n}\n\n/**\n * Device status update event data.\n */\nexport interface DeviceStatusEvent {\n\t/** The sanitized device name. */\n\tdeviceName: string;\n\t/** The IP address of the device. */\n\tip: string;\n\t/** The current status of the device. */\n\tstatus: {\n\t\t/** Current on/off state. */\n\t\tonOff: boolean;\n\t\t/** Current brightness level. */\n\t\tbrightness: number;\n\t\t/** Current color as hex string. */\n\t\tcolor: string;\n\t\t/** Current color temperature in Kelvin. */\n\t\tcolorTemInKelvin: number;\n\t};\n}\n\n/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n *\n * Events:\n * - 'deviceDiscovered': Emitted when a new device is found\n * - 'deviceStatusUpdate': Emitted when device status is updated\n *\n * Usage:\n * const service = new GoveeService(options);\n * service.on('deviceDiscovered', (data) => { ... });\n * service.on('deviceStatusUpdate', (data) => { ... });\n * service.start();\n * // ...\n * service.stop();\n */\nexport class GoveeService extends EventEmitter {\n\tprivate socket: dgram.Socket;\n\tprivate options: GoveeServiceOptions;\n\tprivate devices: { [ip: string]: string } = {};\n\tprivate loggedDevices: string[] = [];\n\tprivate searchInterval?: NodeJS.Timeout;\n\tprivate refreshInterval?: NodeJS.Timeout;\n\n\tstatic readonly LOCAL_PORT = 4002;\n\tstatic readonly SEND_SCAN_PORT = 4001;\n\tstatic readonly CONTROL_PORT = 4003;\n\tstatic readonly M_CAST = '239.255.255.250';\n\tstatic readonly scanMessage = { msg: { cmd: 'scan', data: { account_topic: 'reserved' } } };\n\tstatic readonly requestStatusMessage = { msg: { cmd: 'devStatus', data: {} } };\n\n\t/**\n\t * Create a new GoveeService instance.\n\t *\n\t * @param options Configuration options for the service.\n\t */\n\tconstructor(options: GoveeServiceOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.socket = dgram.createSocket({ type: 'udp4' });\n\t}\n\n\t/**\n\t * Bind UDP socket and start device search/refresh intervals.\n\t */\n\tpublic start(): void {\n\t\tthis.socket.on('message', this.onUdpMessage.bind(this));\n\t\tthis.socket.on('error', (error) => {\n\t\t\tthis.options.logger?.error(`server bind error : ${error.message}`);\n\t\t});\n\t\tthis.socket.bind(\n\t\t\t{ address: this.options.interface, port: GoveeService.LOCAL_PORT },\n\t\t\tthis.serverBound.bind(this),\n\t\t);\n\t}\n\n\tprivate serverBound(): void {\n\t\tthis.socket.setBroadcast(true);\n\t\tthis.socket.setMulticastTTL(128);\n\t\tthis.socket.setMulticastInterface(this.options.interface);\n\t\tthis.socket.addMembership(GoveeService.M_CAST);\n\t\tthis.options.logger?.debug(\n\t\t\t`UDP listening on ${(this.socket.address() as any).address}:${(this.socket.address() as any).port}`,\n\t\t);\n\n\t\t// Add manual IP addresses if provided\n\t\tif (this.options.manualIpAddresses && this.options.manualIpAddresses.length > 0) {\n\t\t\tthis.addManualDevices(this.options.manualIpAddresses);\n\t\t\tthis.options.logger?.info(\n\t\t\t\t`Added ${this.options.manualIpAddresses.length} manual device(s) from configuration.`,\n\t\t\t);\n\t\t}\n\n\t\t// Start auto-discovery if not disabled\n\t\tif (!this.options.disableAutoDiscovery) {\n\t\t\tthis.searchInterval = setInterval(() => this.sendScan(), this.options.searchInterval * 1000);\n\t\t\tthis.options.logger?.debug(`Auto-discovery started with interval ${this.options.searchInterval} seconds.`);\n\t\t} else {\n\t\t\tthis.options.logger?.debug('Auto-discovery is disabled. Only manual devices will be used.');\n\t\t}\n\n\t\tthis.refreshInterval = setInterval(\n\t\t\t() => this.refreshAllDevices(),\n\t\t\tthis.options.deviceStatusRefreshInterval * 1000,\n\t\t);\n\n\t\t// Emit serviceStarted event after successful startup\n\t\tthis.emit('serviceStarted');\n\t}\n\n\t/**\n\t * Handle incoming UDP messages.\n\t *\n\t * @param message The message buffer.\n\t * @param remote The sender info.\n\t */\n\tprivate onUdpMessage(message: Buffer, remote: dgram.RemoteInfo): void {\n\t\tconst messageObject = JSON.parse(message.toString());\n\t\tswitch (messageObject.msg.cmd) {\n\t\t\tcase 'scan': {\n\t\t\t\tif (messageObject.msg.data.device) {\n\t\t\t\t\tconst deviceName = messageObject.msg.data.device.replace(\n\t\t\t\t\t\tthis.options.forbiddenChars ?? /[^a-zA-Z0-9_-]/g,\n\t\t\t\t\t\t'_',\n\t\t\t\t\t);\n\t\t\t\t\tthis.devices[remote.address] = deviceName;\n\t\t\t\t\tif (this.options.extendedLogging) {\n\t\t\t\t\t\tthis.options.logger?.info(\n\t\t\t\t\t\t\t`Discovered device: ${deviceName} at ${remote.address} (model: ${messageObject.msg.data.sku})`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthis.emit('deviceDiscovered', {\n\t\t\t\t\t\tip: remote.address,\n\t\t\t\t\t\tdeviceName: deviceName,\n\t\t\t\t\t\tdeviceModel: messageObject.msg.data.sku,\n\t\t\t\t\t} as DeviceDiscoveryEvent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'devStatus': {\n\t\t\t\tconst sendingDevice = this.devices[remote.address];\n\t\t\t\tif (sendingDevice) {\n\t\t\t\t\tif (this.options.extendedLogging && !this.loggedDevices.includes(remote.address.toString())) {\n\t\t\t\t\t\tthis.options.logger?.info(`device status message data: ${JSON.stringify(messageObject)}`);\n\t\t\t\t\t\tthis.loggedDevices.push(remote.address.toString());\n\t\t\t\t\t}\n\t\t\t\t\tthis.emitDeviceStatusUpdate(sendingDevice, remote.address, messageObject);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthis.options.logger?.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to all known devices.\n\t */\n\tpublic refreshAllDevices(): void {\n\t\tfor (const ip in this.devices) {\n\t\t\tthis.requestDeviceStatus(ip);\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to a specific device.\n\t *\n\t * @param receiver The IP address or hostname of the device.\n\t */\n\tpublic requestDeviceStatus(receiver: string): void {\n\t\tconst requestDeviceStatusBuffer = Buffer.from(JSON.stringify(GoveeService.requestStatusMessage));\n\t\tthis.socket.send(\n\t\t\trequestDeviceStatusBuffer,\n\t\t\t0,\n\t\t\trequestDeviceStatusBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send scan message to the UDP multicast address.\n\t */\n\tpublic sendScan(): void {\n\t\tif (this.options.extendedLogging) {\n\t\t\tthis.options.logger?.debug('sending scan message');\n\t\t}\n\t\tconst scanMessageBuffer = Buffer.from(JSON.stringify(GoveeService.scanMessage));\n\t\tthis.socket.send(\n\t\t\tscanMessageBuffer,\n\t\t\t0,\n\t\t\tscanMessageBuffer.length,\n\t\t\tGoveeService.SEND_SCAN_PORT,\n\t\t\tGoveeService.M_CAST,\n\t\t);\n\t}\n\n\t/**\n\t * Add manual devices by IP address without discovery.\n\t * These devices will be added to the device list and can be controlled.\n\t *\n\t * @param ipAddresses Array of IP addresses to add as manual devices.\n\t */\n\tpublic addManualDevices(ipAddresses: string[]): void {\n\t\tfor (const ip of ipAddresses) {\n\t\t\tif (!ip || ip.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst trimmedIp = ip.trim();\n\n\t\t\t// Validate IP address format\n\t\t\tif (!isValidIpAddress(trimmedIp)) {\n\t\t\t\tthis.options.logger?.error(`Invalid IP address format: \"${trimmedIp}\" - skipping this entry`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Generate a device name based on the IP address\n\t\t\tconst deviceName = `Manual_${trimmedIp.replace(/\\./g, '_')}`;\n\t\t\tthis.devices[trimmedIp] = deviceName;\n\t\t\tthis.options.logger?.info(`Added manual device: ${deviceName} at ${trimmedIp}`);\n\n\t\t\t// Emit discovery event for manual device\n\t\t\tthis.emit('deviceDiscovered', {\n\t\t\t\tip: trimmedIp,\n\t\t\t\tdeviceName: deviceName,\n\t\t\t} as DeviceDiscoveryEvent);\n\n\t\t\t// Request initial status\n\t\t\tthis.requestDeviceStatus(trimmedIp);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all intervals and close the socket.\n\t */\n\tpublic stop(): void {\n\t\tthis.options.logger?.debug('Stopping GoveeService and closing UDP socket.');\n\t\tif (this.searchInterval) {\n\t\t\tclearInterval(this.searchInterval);\n\t\t}\n\t\tif (this.refreshInterval) {\n\t\t\tclearInterval(this.refreshInterval);\n\t\t}\n\t\tthis.socket.close();\n\t}\n\n\t/**\n\t * Get all known devices.\n\t */\n\tpublic getDevices(): { [ip: string]: string } {\n\t\treturn this.devices;\n\t}\n\n\t/**\n\t * Handles a state change and sends the appropriate command to the device.\n\t *\n\t * @param id The state ID\n\t * @param state The new state object\n\t * @param receiver The device IP as string\n\t */\n\tpublic handleStateChange(id: string, state: ioBroker.State, receiver: string): void {\n\t\tconst stateKey = id.split('.')[4];\n\t\tswitch (stateKey) {\n\t\t\tcase 'onOff':\n\t\t\t\tthis.sendTurnCommand(receiver, !!state.val);\n\t\t\t\tbreak;\n\t\t\tcase 'brightness':\n\t\t\t\tthis.sendBrightnessCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'colorTemInKelvin':\n\t\t\t\tthis.sendColorTempCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'color': {\n\t\t\t\tconst colorValue = state.val?.toString();\n\t\t\t\tif (colorValue) {\n\t\t\t\t\tthis.sendColorCommand(receiver, colorValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * Send a turn on/off command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value true for on, false for off\n\t */\n\tpublic sendTurnCommand(receiver: string, value: boolean): void {\n\t\tconst turnMessage = { msg: { cmd: 'turn', data: { value: value ? 1 : 0 } } };\n\t\tconst turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));\n\t\tthis.socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a brightness command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value Brightness value\n\t */\n\tpublic sendBrightnessCommand(receiver: string, value: number): void {\n\t\tconst brightnessMessage = { msg: { cmd: 'brightness', data: { value } } };\n\t\tconst brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));\n\t\tthis.socket.send(\n\t\t\tbrightnessMessageBuffer,\n\t\t\t0,\n\t\t\tbrightnessMessageBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send a color temperature command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param kelvin Color temperature in Kelvin\n\t */\n\tpublic sendColorTempCommand(receiver: string, kelvin: number): void {\n\t\tconst colorTempMessageBuffer = Buffer.from(\n\t\t\tJSON.stringify({\n\t\t\t\tmsg: {\n\t\t\t\t\tcmd: 'colorwc',\n\t\t\t\t\tdata: { color: { r: 0, g: 0, b: 0 }, colorTemInKelvin: kelvin },\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tthis.socket.send(colorTempMessageBuffer, 0, colorTempMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a color command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param hexColor Color as hex string (e.g. #FFAABB)\n\t */\n\tpublic sendColorCommand(receiver: string, hexColor: string): void {\n\t\tconst rgb = hexToRgb(hexColor);\n\t\tconst colorMessage = { msg: { cmd: 'colorwc', data: { color: rgb } } };\n\t\tconst colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));\n\t\tthis.socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Emit device status update event with parsed status data.\n\t *\n\t * @param deviceName The device name.\n\t * @param ip The device IP address.\n\t * @param messageObject The parsed message object containing device status.\n\t */\n\tprivate emitDeviceStatusUpdate(deviceName: string, ip: string, messageObject: any): void {\n\t\tconst deviceData = messageObject.msg.data;\n\t\tconst colorString = `#${componentToHex(deviceData.color.r)}${componentToHex(deviceData.color.g)}${componentToHex(deviceData.color.b)}`;\n\n\t\tthis.emit('deviceStatusUpdate', {\n\t\t\tdeviceName: deviceName,\n\t\t\tip: ip,\n\t\t\tstatus: {\n\t\t\t\tonOff: deviceData.onOff === 1,\n\t\t\t\tbrightness: deviceData.brightness,\n\t\t\t\tcolor: colorString,\n\t\t\t\tcolorTemInKelvin: deviceData.colorTemInKelvin,\n\t\t\t},\n\t\t} as DeviceStatusEvent);\n\t}\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n */\n\nimport * as dgram from 'node:dgram';\nimport { EventEmitter } from 'node:events';\nimport type { GoveeServiceOptions } from './goveeServiceOptions';\nimport { componentToHex, hexToRgb } from './tools/hexTool';\nimport { isValidIpAddress } from './tools/ipValidation';\n\n/**\n * Device discovery event data.\n */\nexport interface DeviceDiscoveryEvent {\n\t/** The IP address of the discovered device. */\n\tip: string;\n\t/** The sanitized device name. */\n\tdeviceName: string;\n\t/** The device model. */\n\tdeviceModel: string;\n}\n\n/**\n * Device status update event data.\n */\nexport interface DeviceStatusEvent {\n\t/** The sanitized device name. */\n\tdeviceName: string;\n\t/** The IP address of the device. */\n\tip: string;\n\t/** The current status of the device. */\n\tstatus: {\n\t\t/** Current on/off state. */\n\t\tonOff: boolean;\n\t\t/** Current brightness level. */\n\t\tbrightness: number;\n\t\t/** Current color as hex string. */\n\t\tcolor: string;\n\t\t/** Current color temperature in Kelvin. */\n\t\tcolorTemInKelvin: number;\n\t};\n}\n\n/**\n * GoveeService handles all business logic for device discovery, status updates, and UDP communication.\n * This class is independent from ioBroker.Adapter and can be tested separately.\n *\n * Events:\n * - 'deviceDiscovered': Emitted when a new device is found\n * - 'deviceStatusUpdate': Emitted when device status is updated\n *\n * Usage:\n * const service = new GoveeService(options);\n * service.on('deviceDiscovered', (data) => { ... });\n * service.on('deviceStatusUpdate', (data) => { ... });\n * service.start();\n * // ...\n * service.stop();\n */\nexport class GoveeService extends EventEmitter {\n\tprivate socket: dgram.Socket;\n\tprivate options: GoveeServiceOptions;\n\tprivate devices: { [ip: string]: string } = {};\n\tprivate loggedDevices: string[] = [];\n\tprivate searchInterval?: NodeJS.Timeout;\n\tprivate refreshInterval?: NodeJS.Timeout;\n\tprivate scanMode: 'interval' | 'once' | 'never' = 'interval';\n\n\tstatic readonly LOCAL_PORT = 4002;\n\tstatic readonly SEND_SCAN_PORT = 4001;\n\tstatic readonly CONTROL_PORT = 4003;\n\tstatic readonly M_CAST = '239.255.255.250';\n\tstatic readonly scanMessage = { msg: { cmd: 'scan', data: { account_topic: 'reserved' } } };\n\tstatic readonly requestStatusMessage = { msg: { cmd: 'devStatus', data: {} } };\n\n\t/**\n\t * Create a new GoveeService instance.\n\t *\n\t * @param options Configuration options for the service.\n\t */\n\tconstructor(options: GoveeServiceOptions) {\n\t\tsuper();\n\t\tthis.options = options;\n\t\tthis.socket = dgram.createSocket({ type: 'udp4' });\n\t\t// scanMode aus options \u00FCbernehmen, fallback auf interval\n\t\tif ((options as any).scanMode === 'once' || (options as any).scanMode === 'never') {\n\t\t\tthis.scanMode = (options as any).scanMode;\n\t\t} else {\n\t\t\tthis.scanMode = 'interval';\n\t\t}\n\t}\n\n\t/**\n\t * Bind UDP socket and start device search/refresh intervals.\n\t */\n\tpublic start(): void {\n\t\tthis.socket.on('message', this.onUdpMessage.bind(this));\n\t\tthis.socket.on('error', (error) => {\n\t\t\tthis.options.logger?.error(`server bind error : ${error.message}`);\n\t\t});\n\t\tthis.socket.bind(\n\t\t\t{ address: this.options.interface, port: GoveeService.LOCAL_PORT },\n\t\t\tthis.serverBound.bind(this),\n\t\t);\n\t}\n\n\tprivate serverBound(): void {\n\t\tthis.socket.setBroadcast(true);\n\t\tthis.socket.setMulticastTTL(128);\n\t\tthis.socket.setMulticastInterface(this.options.interface);\n\t\tthis.socket.addMembership(GoveeService.M_CAST);\n\t\tthis.options.logger?.debug(\n\t\t\t`UDP listening on ${(this.socket.address() as any).address}:${(this.socket.address() as any).port}`,\n\t\t);\n\n\t\t// Add manual IP addresses if provided\n\t\tif (this.options.manualIpAddresses && this.options.manualIpAddresses.length > 0) {\n\t\t\tthis.addManualDevices(this.options.manualIpAddresses);\n\t\t\tthis.options.logger?.info(\n\t\t\t\t`Added ${this.options.manualIpAddresses.length} manual device(s) from configuration.`,\n\t\t\t);\n\t\t}\n\n\t\t// Discovery according to the user's preference (scanMode)\n\t\tif (this.scanMode === 'interval') {\n\t\t\tthis.searchInterval = setInterval(() => this.sendScan(), this.options.searchInterval * 1000);\n\t\t\tthis.options.logger?.debug(`Auto-discovery started with interval ${this.options.searchInterval} seconds.`);\n\t\t} else if (this.scanMode === 'once') {\n\t\t\tthis.sendScan();\n\t\t\tthis.options.logger?.debug('Auto-discovery: scan once at startup.');\n\t\t} else if (this.scanMode === 'never') {\n\t\t\tthis.options.logger?.debug('Auto-discovery is disabled. Only manual devices will be used.');\n\t\t}\n\n\t\tthis.refreshInterval = setInterval(\n\t\t\t() => this.refreshAllDevices(),\n\t\t\tthis.options.deviceStatusRefreshInterval * 1000,\n\t\t);\n\n\t\t// Emit serviceStarted event after successful startup\n\t\tthis.emit('serviceStarted');\n\t}\n\n\t/**\n\t * Handle incoming UDP messages.\n\t *\n\t * @param message The message buffer.\n\t * @param remote The sender info.\n\t */\n\tprivate onUdpMessage(message: Buffer, remote: dgram.RemoteInfo): void {\n\t\tlet messageObject: any;\n\t\ttry {\n\t\t\tmessageObject = JSON.parse(message.toString());\n\t\t} catch (err) {\n\t\t\tthis.options.logger?.error(\n\t\t\t\t`Malformed UDP message from ${remote.address}:${remote.port}: ${err instanceof Error ? err.message : String(err)}`,\n\t\t\t);\n\t\t\tthrow err;\n\t\t}\n\t\tswitch (messageObject.msg.cmd) {\n\t\t\tcase 'scan': {\n\t\t\t\tif (messageObject.msg.data.device) {\n\t\t\t\t\tconst deviceName = messageObject.msg.data.device.replace(\n\t\t\t\t\t\tthis.options.forbiddenChars ?? /[^a-zA-Z0-9_-]/g,\n\t\t\t\t\t\t'_',\n\t\t\t\t\t);\n\t\t\t\t\tthis.devices[remote.address] = deviceName;\n\t\t\t\t\tif (this.options.extendedLogging) {\n\t\t\t\t\t\tthis.options.logger?.info(\n\t\t\t\t\t\t\t`Discovered device: ${deviceName} at ${remote.address} (model: ${messageObject.msg.data.sku})`,\n\t\t\t\t\t\t);\n\t\t\t\t\t}\n\t\t\t\t\tthis.emit('deviceDiscovered', {\n\t\t\t\t\t\tip: remote.address,\n\t\t\t\t\t\tdeviceName: deviceName,\n\t\t\t\t\t\tdeviceModel: messageObject.msg.data.sku,\n\t\t\t\t\t} as DeviceDiscoveryEvent);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tcase 'devStatus': {\n\t\t\t\tconst sendingDevice = this.devices[remote.address];\n\t\t\t\tif (sendingDevice) {\n\t\t\t\t\tif (this.options.extendedLogging && !this.loggedDevices.includes(remote.address.toString())) {\n\t\t\t\t\t\tthis.options.logger?.info(`device status message data: ${JSON.stringify(messageObject)}`);\n\t\t\t\t\t\tthis.loggedDevices.push(remote.address.toString());\n\t\t\t\t\t}\n\t\t\t\t\tthis.emitDeviceStatusUpdate(sendingDevice, remote.address, messageObject);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tdefault: {\n\t\t\t\tthis.options.logger?.debug(`message from: ${remote.address}:${remote.port} - ${message.toString()}`);\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to all known devices.\n\t */\n\tpublic refreshAllDevices(): void {\n\t\tfor (const ip in this.devices) {\n\t\t\tthis.requestDeviceStatus(ip);\n\t\t}\n\t}\n\n\t/**\n\t * Send device status request to a specific device.\n\t *\n\t * @param receiver The IP address or hostname of the device.\n\t */\n\tpublic requestDeviceStatus(receiver: string): void {\n\t\tconst requestDeviceStatusBuffer = Buffer.from(JSON.stringify(GoveeService.requestStatusMessage));\n\t\tthis.socket.send(\n\t\t\trequestDeviceStatusBuffer,\n\t\t\t0,\n\t\t\trequestDeviceStatusBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send scan message to the UDP multicast address.\n\t */\n\tpublic sendScan(): void {\n\t\tif (this.options.extendedLogging) {\n\t\t\tthis.options.logger?.debug('sending scan message');\n\t\t}\n\t\tconst scanMessageBuffer = Buffer.from(JSON.stringify(GoveeService.scanMessage));\n\t\tthis.socket.send(\n\t\t\tscanMessageBuffer,\n\t\t\t0,\n\t\t\tscanMessageBuffer.length,\n\t\t\tGoveeService.SEND_SCAN_PORT,\n\t\t\tGoveeService.M_CAST,\n\t\t);\n\t}\n\n\t/**\n\t * Add manual devices by IP address without discovery.\n\t * These devices will be added to the device list and can be controlled.\n\t *\n\t * @param ipAddresses Array of IP addresses to add as manual devices.\n\t */\n\tpublic addManualDevices(ipAddresses: string[]): void {\n\t\tfor (const ip of ipAddresses) {\n\t\t\tif (!ip || ip.trim().length === 0) {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tconst trimmedIp = ip.trim();\n\n\t\t\t// Validate IP address format\n\t\t\tif (!isValidIpAddress(trimmedIp)) {\n\t\t\t\tthis.options.logger?.error(`Invalid IP address format: \"${trimmedIp}\" - skipping this entry`);\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\t// Generate a device name based on the IP address\n\t\t\tconst deviceName = `Manual_${trimmedIp.replace(/\\./g, '_')}`;\n\t\t\tthis.devices[trimmedIp] = deviceName;\n\t\t\tthis.options.logger?.info(`Added manual device: ${deviceName} at ${trimmedIp}`);\n\n\t\t\t// Emit discovery event for manual device\n\t\t\tthis.emit('deviceDiscovered', {\n\t\t\t\tip: trimmedIp,\n\t\t\t\tdeviceName: deviceName,\n\t\t\t} as DeviceDiscoveryEvent);\n\n\t\t\t// Request initial status\n\t\t\tthis.requestDeviceStatus(trimmedIp);\n\t\t}\n\t}\n\n\t/**\n\t * Stop all intervals and close the socket.\n\t */\n\tpublic stop(): void {\n\t\tthis.options.logger?.debug('Stopping GoveeService and closing UDP socket.');\n\t\tif (this.searchInterval) {\n\t\t\tclearInterval(this.searchInterval);\n\t\t}\n\t\tif (this.refreshInterval) {\n\t\t\tclearInterval(this.refreshInterval);\n\t\t}\n\t\tthis.socket.close();\n\t}\n\n\t/**\n\t * Get all known devices.\n\t */\n\tpublic getDevices(): { [ip: string]: string } {\n\t\treturn this.devices;\n\t}\n\n\t/**\n\t * Handles a state change and sends the appropriate command to the device.\n\t *\n\t * @param id The state ID\n\t * @param state The new state object\n\t * @param receiver The device IP as string\n\t */\n\tpublic handleStateChange(id: string, state: ioBroker.State, receiver: string): void {\n\t\tconst stateKey = id.split('.')[4];\n\t\tswitch (stateKey) {\n\t\t\tcase 'onOff':\n\t\t\t\tthis.sendTurnCommand(receiver, !!state.val);\n\t\t\t\tbreak;\n\t\t\tcase 'brightness':\n\t\t\t\tthis.sendBrightnessCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'colorTemInKelvin':\n\t\t\t\tthis.sendColorTempCommand(receiver, Number(state.val));\n\t\t\t\tbreak;\n\t\t\tcase 'color': {\n\t\t\t\tconst colorValue = state.val?.toString();\n\t\t\t\tif (colorValue) {\n\t\t\t\t\tthis.sendColorCommand(receiver, colorValue);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t}\n\t/**\n\t * Send a turn on/off command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value true for on, false for off\n\t */\n\tpublic sendTurnCommand(receiver: string, value: boolean): void {\n\t\tconst turnMessage = { msg: { cmd: 'turn', data: { value: value ? 1 : 0 } } };\n\t\tconst turnMessageBuffer = Buffer.from(JSON.stringify(turnMessage));\n\t\tthis.socket.send(turnMessageBuffer, 0, turnMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a brightness command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param value Brightness value\n\t */\n\tpublic sendBrightnessCommand(receiver: string, value: number): void {\n\t\tconst brightnessMessage = { msg: { cmd: 'brightness', data: { value } } };\n\t\tconst brightnessMessageBuffer = Buffer.from(JSON.stringify(brightnessMessage));\n\t\tthis.socket.send(\n\t\t\tbrightnessMessageBuffer,\n\t\t\t0,\n\t\t\tbrightnessMessageBuffer.length,\n\t\t\tGoveeService.CONTROL_PORT,\n\t\t\treceiver,\n\t\t);\n\t}\n\n\t/**\n\t * Send a color temperature command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param kelvin Color temperature in Kelvin\n\t */\n\tpublic sendColorTempCommand(receiver: string, kelvin: number): void {\n\t\tconst colorTempMessageBuffer = Buffer.from(\n\t\t\tJSON.stringify({\n\t\t\t\tmsg: {\n\t\t\t\t\tcmd: 'colorwc',\n\t\t\t\t\tdata: { color: { r: 0, g: 0, b: 0 }, colorTemInKelvin: kelvin },\n\t\t\t\t},\n\t\t\t}),\n\t\t);\n\t\tthis.socket.send(colorTempMessageBuffer, 0, colorTempMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Send a color command to a device.\n\t *\n\t * @param receiver IP address or hostname\n\t * @param hexColor Color as hex string (e.g. #FFAABB)\n\t */\n\tpublic sendColorCommand(receiver: string, hexColor: string): void {\n\t\tconst rgb = hexToRgb(hexColor);\n\t\tconst colorMessage = { msg: { cmd: 'colorwc', data: { color: rgb } } };\n\t\tconst colorMessageBuffer = Buffer.from(JSON.stringify(colorMessage));\n\t\tthis.socket.send(colorMessageBuffer, 0, colorMessageBuffer.length, GoveeService.CONTROL_PORT, receiver);\n\t}\n\n\t/**\n\t * Emit device status update event with parsed status data.\n\t *\n\t * @param deviceName The device name.\n\t * @param ip The device IP address.\n\t * @param messageObject The parsed message object containing device status.\n\t */\n\tprivate emitDeviceStatusUpdate(deviceName: string, ip: string, messageObject: any): void {\n\t\tconst deviceData = messageObject.msg.data;\n\t\t// Robust: Fallback f\u00FCr fehlende oder ung\u00FCltige Farbdaten\n\t\tlet colorString = '#000000';\n\t\tif (deviceData.color && typeof deviceData.color === 'object') {\n\t\t\tconst r = typeof deviceData.color.r === 'number' ? deviceData.color.r : 0;\n\t\t\tconst g = typeof deviceData.color.g === 'number' ? deviceData.color.g : 0;\n\t\t\tconst b = typeof deviceData.color.b === 'number' ? deviceData.color.b : 0;\n\t\t\tcolorString = `#${componentToHex(r)}${componentToHex(g)}${componentToHex(b)}`;\n\t\t}\n\t\tthis.emit('deviceStatusUpdate', {\n\t\t\tdeviceName: deviceName,\n\t\t\tip: ip,\n\t\t\tstatus: {\n\t\t\t\tonOff: deviceData.onOff === 1,\n\t\t\t\tbrightness: deviceData.brightness,\n\t\t\t\tcolor: colorString,\n\t\t\t\tcolorTemInKelvin: deviceData.colorTemInKelvin,\n\t\t\t},\n\t\t} as DeviceStatusEvent);\n\t}\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/build/main.js CHANGED
@@ -83,7 +83,7 @@ class GoveeLocal extends utils.Adapter {
83
83
  searchInterval: this.config.searchInterval,
84
84
  deviceStatusRefreshInterval: this.config.deviceStatusRefreshInterval,
85
85
  extendedLogging: this.config.extendedLogging,
86
- forbiddenChars: /[^a-zA-Z0-9_-]/g,
86
+ forbiddenChars: this.FORBIDDEN_CHARS,
87
87
  manualIpAddresses: this.config.manualIpTable,
88
88
  disableAutoDiscovery: this.config.disableAutoDiscovery,
89
89
  logger: {
package/build/main.js.map CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/main.ts"],
4
- "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.3.0\n */\n\nimport * as utils from '@iobroker/adapter-core';\nimport { GoveeService, type DeviceDiscoveryEvent, type DeviceStatusEvent } from './lib/goveeService';\n\n/**\n * Main adapter class for ioBroker Govee Local.\n * Handles initialization, event wiring, and communication with GoveeService.\n */\nexport class GoveeLocal extends utils.Adapter {\n\t/** Instance of GoveeService for device communication */\n\tprivate goveeService!: GoveeService;\n\t/**\n\t * Adapter constructor. Registers lifecycle event handlers.\n\t *\n\t * @param options Optional adapter options to override defaults.\n\t */\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: 'govee-local',\n\t\t});\n\t\t// Register event handlers for adapter lifecycle\n\t\tthis.on('ready', this.onReady.bind(this));\n\t\tthis.on('stateChange', this.onStateChange.bind(this));\n\t\t// Uncomment if you want to handle object changes or messages:\n\t\t// this.on('objectChange', this.onObjectChange.bind(this));\n\t\t// this.on('message', this.onMessage.bind(this));\n\t\tthis.on('unload', this.onUnload.bind(this));\n\t}\n\n\t/**\n\t * Called when databases are connected and adapter received configuration.\n\t * Initializes GoveeService and sets up event listeners for device events.\n\t */\n\tprivate async onReady(): Promise<void> {\n\t\t// Create connection indicator state if it doesn't exist\n\t\tvoid this.setObjectNotExists('info.connection', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device discovery running',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'indicator.connected',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create state for discovered devices\n\t\tvoid this.setObjectNotExists('info.discoveredDevices', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'List of discovered devices',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'json',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t\tdesc: 'JSON object with IP addresses and device names of all discovered devices',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Initialize GoveeService with adapter config and logger\n\t\tthis.goveeService = new GoveeService({\n\t\t\tinterface: this.config.interface,\n\t\t\tsearchInterval: this.config.searchInterval,\n\t\t\tdeviceStatusRefreshInterval: this.config.deviceStatusRefreshInterval,\n\t\t\textendedLogging: this.config.extendedLogging,\n\t\t\tforbiddenChars: /[^a-zA-Z0-9_-]/g,\n\t\t\tmanualIpAddresses: this.config.manualIpTable,\n\t\t\tdisableAutoDiscovery: this.config.disableAutoDiscovery,\n\t\t\tlogger: {\n\t\t\t\tdebug: (msg) => this.log.debug(msg),\n\t\t\t\tinfo: (msg) => this.log.info(msg),\n\t\t\t\terror: (msg) => this.log.error(msg),\n\t\t\t},\n\t\t});\n\n\t\t// Listen for device discovery and status update events\n\t\tthis.goveeService.on('deviceDiscovered', (data) => {\n\t\t\tvoid this.handleDeviceDiscovered(data);\n\t\t});\n\n\t\tthis.goveeService.on('deviceStatusUpdate', (data) => {\n\t\t\tvoid this.handleDeviceStatusUpdate(data);\n\t\t});\n\n\t\t// Listen for serviceStarted event to set connection state to true\n\t\tthis.goveeService.on('serviceStarted', () => {\n\t\t\tvoid this.setState('info.connection', { val: true, ack: true });\n\t\t});\n\n\t\t// Start device discovery and status polling\n\t\tthis.goveeService.start();\n\n\t\tif (this.config.extendedLogging) {\n\t\t\tthis.log.debug('running with extended logging');\n\t\t}\n\n\t\t// Subscribe to all device status state changes\n\t\tvoid this.subscribeStates('*.devStatus.*');\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Called if a subscribed state changes (e.g. user toggles a switch in ioBroker UI).\n\t * Forwards the change to the GoveeService for device communication.\n\t *\n\t * @param id The ID of the changed state.\n\t * @param state The new state object or null/undefined.\n\t */\n\tprivate async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n\t\tif (state && !state.ack) {\n\t\t\t// Extract device name from state ID and get its IP address\n\t\t\tconst ipOfDevice = await this.getStateAsync(`${id.split('.')[2]}.deviceInfo.ip`);\n\t\t\tconst receiver = ipOfDevice?.val?.toString();\n\t\t\tif (typeof receiver === 'string') {\n\t\t\t\t// Forward the state change to the GoveeService\n\t\t\t\tthis.goveeService.handleStateChange(id, state, receiver);\n\t\t\t} else {\n\t\t\t\tthis.log.error('device not found or IP is not a string');\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\t/**\n\t * Called when the adapter shuts down. Cleans up resources and stops services.\n\t *\n\t * @param callback Callback function after unload process.\n\t */\n\tprivate onUnload(callback: () => void): void {\n\t\ttry {\n\t\t\tif (this.goveeService) {\n\t\t\t\tthis.goveeService.removeAllListeners();\n\t\t\t\tthis.goveeService.stop();\n\t\t\t}\n\t\t\t// Set connection state to false\n\t\t\tvoid this.updateStateAsync('info.connection', false);\n\t\t\tcallback();\n\t\t} catch (e: any) {\n\t\t\tthis.log.error(e.message);\n\t\t\tcallback();\n\t\t}\n\t}\n\n\t/**\n\t * Updates a state only if the value has changed.\n\t *\n\t * @param fullName Full object path\n\t * @param state New value\n\t * @param acknowledged Whether the value is acknowledged (default: true)\n\t */\n\tprivate async updateStateAsync(fullName: string, state: any, acknowledged = true): Promise<void> {\n\t\tconst currentState = await this.getStateAsync(fullName);\n\t\tif (currentState?.val !== state) {\n\t\t\tvoid this.setState(fullName, {\n\t\t\t\tval: state,\n\t\t\t\tack: acknowledged,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Handles device discovery event from GoveeService.\n\t * Creates all necessary objects and states for the new device.\n\t *\n\t * @param event The device discovery event data.\n\t */\n\tprivate async handleDeviceDiscovered(event: DeviceDiscoveryEvent): Promise<void> {\n\t\tconst { ip, deviceName, deviceModel } = event;\n\n\t\t// Create device folder\n\t\tawait this.setObjectNotExistsAsync(deviceName, {\n\t\t\ttype: 'device',\n\t\t\tcommon: {\n\t\t\t\tname: deviceModel,\n\t\t\t\trole: 'group',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create deviceInfo folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Info',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create IP address state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo.ip`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'IP address of the Lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'info.ip',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.deviceInfo.ip`, ip);\n\n\t\t// Create devStatus folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Status',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\tthis.log.info(`Device discovered: ${deviceName} at ${ip}`);\n\n\t\t// Update the list of discovered devices\n\t\tawait this.updateDiscoveredDevicesList();\n\t}\n\n\t/**\n\t * Updates the info.discoveredDevices state with the current list of all discovered devices.\n\t */\n\tprivate async updateDiscoveredDevicesList(): Promise<void> {\n\t\tconst devices = this.goveeService.getDevices();\n\t\tawait this.setStateAsync('info.discoveredDevices', {\n\t\t\tval: JSON.stringify(devices, null, 2),\n\t\t\tack: true,\n\t\t});\n\t}\n\n\t/**\n\t * Handles device status update event from GoveeService.\n\t * Creates and updates all relevant states for the device.\n\t *\n\t * @param event The device status update event data.\n\t */\n\tprivate async handleDeviceStatusUpdate(event: DeviceStatusEvent): Promise<void> {\n\t\tconst { deviceName, status } = event;\n\n\t\t// Create and update onOff state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.onOff`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'On / Off state of the lamp',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'switch',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.onOff`, status.onOff);\n\n\t\t// Create and update brightness state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.brightness`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Brightness of the light',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.dimmer',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.brightness`, status.brightness);\n\n\t\t// Create and update color state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.color`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Current showing color of the lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'level.color.rgb',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.color`, status.color);\n\n\t\t// Create and update color temperature state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.colorTemInKelvin`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'If staying in white light, the color temperature',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.color.temperature',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.colorTemInKelvin`, status.colorTemInKelvin);\n\t}\n}\n\n// Export factory function for ioBroker or start instance directly\nif (require.main !== module) {\n\t// Export factory function for ioBroker, also available as ES6 export\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new GoveeLocal(options);\n} else {\n\t// Otherwise start the instance directly\n\t(() => new GoveeLocal())();\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/*\n * Created with @iobroker/create-adapter v2.3.0\n */\n\nimport * as utils from '@iobroker/adapter-core';\nimport { GoveeService, type DeviceDiscoveryEvent, type DeviceStatusEvent } from './lib/goveeService';\n\n/**\n * Main adapter class for ioBroker Govee Local.\n * Handles initialization, event wiring, and communication with GoveeService.\n */\nexport class GoveeLocal extends utils.Adapter {\n\t/** Instance of GoveeService for device communication */\n\tprivate goveeService!: GoveeService;\n\t/**\n\t * Adapter constructor. Registers lifecycle event handlers.\n\t *\n\t * @param options Optional adapter options to override defaults.\n\t */\n\tpublic constructor(options: Partial<utils.AdapterOptions> = {}) {\n\t\tsuper({\n\t\t\t...options,\n\t\t\tname: 'govee-local',\n\t\t});\n\t\t// Register event handlers for adapter lifecycle\n\t\tthis.on('ready', this.onReady.bind(this));\n\t\tthis.on('stateChange', this.onStateChange.bind(this));\n\t\t// Uncomment if you want to handle object changes or messages:\n\t\t// this.on('objectChange', this.onObjectChange.bind(this));\n\t\t// this.on('message', this.onMessage.bind(this));\n\t\tthis.on('unload', this.onUnload.bind(this));\n\t}\n\n\t/**\n\t * Called when databases are connected and adapter received configuration.\n\t * Initializes GoveeService and sets up event listeners for device events.\n\t */\n\tprivate async onReady(): Promise<void> {\n\t\t// Create connection indicator state if it doesn't exist\n\t\tvoid this.setObjectNotExists('info.connection', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device discovery running',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'indicator.connected',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create state for discovered devices\n\t\tvoid this.setObjectNotExists('info.discoveredDevices', {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'List of discovered devices',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'json',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t\tdesc: 'JSON object with IP addresses and device names of all discovered devices',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Initialize GoveeService with adapter config and logger\n\t\tthis.goveeService = new GoveeService({\n\t\t\tinterface: this.config.interface,\n\t\t\tsearchInterval: this.config.searchInterval,\n\t\t\tdeviceStatusRefreshInterval: this.config.deviceStatusRefreshInterval,\n\t\t\textendedLogging: this.config.extendedLogging,\n\t\t\tforbiddenChars: this.FORBIDDEN_CHARS,\n\t\t\tmanualIpAddresses: this.config.manualIpTable,\n\t\t\tdisableAutoDiscovery: this.config.disableAutoDiscovery,\n\t\t\tlogger: {\n\t\t\t\tdebug: (msg) => this.log.debug(msg),\n\t\t\t\tinfo: (msg) => this.log.info(msg),\n\t\t\t\terror: (msg) => this.log.error(msg),\n\t\t\t},\n\t\t});\n\n\t\t// Listen for device discovery and status update events\n\t\tthis.goveeService.on('deviceDiscovered', (data) => {\n\t\t\tvoid this.handleDeviceDiscovered(data);\n\t\t});\n\n\t\tthis.goveeService.on('deviceStatusUpdate', (data) => {\n\t\t\tvoid this.handleDeviceStatusUpdate(data);\n\t\t});\n\n\t\t// Listen for serviceStarted event to set connection state to true\n\t\tthis.goveeService.on('serviceStarted', () => {\n\t\t\tvoid this.setState('info.connection', { val: true, ack: true });\n\t\t});\n\n\t\t// Start device discovery and status polling\n\t\tthis.goveeService.start();\n\n\t\tif (this.config.extendedLogging) {\n\t\t\tthis.log.debug('running with extended logging');\n\t\t}\n\n\t\t// Subscribe to all device status state changes\n\t\tvoid this.subscribeStates('*.devStatus.*');\n\t\treturn Promise.resolve();\n\t}\n\n\t/**\n\t * Called if a subscribed state changes (e.g. user toggles a switch in ioBroker UI).\n\t * Forwards the change to the GoveeService for device communication.\n\t *\n\t * @param id The ID of the changed state.\n\t * @param state The new state object or null/undefined.\n\t */\n\tprivate async onStateChange(id: string, state: ioBroker.State | null | undefined): Promise<void> {\n\t\tif (state && !state.ack) {\n\t\t\t// Extract device name from state ID and get its IP address\n\t\t\tconst ipOfDevice = await this.getStateAsync(`${id.split('.')[2]}.deviceInfo.ip`);\n\t\t\tconst receiver = ipOfDevice?.val?.toString();\n\t\t\tif (typeof receiver === 'string') {\n\t\t\t\t// Forward the state change to the GoveeService\n\t\t\t\tthis.goveeService.handleStateChange(id, state, receiver);\n\t\t\t} else {\n\t\t\t\tthis.log.error('device not found or IP is not a string');\n\t\t\t}\n\t\t}\n\t\treturn Promise.resolve();\n\t}\n\t/**\n\t * Called when the adapter shuts down. Cleans up resources and stops services.\n\t *\n\t * @param callback Callback function after unload process.\n\t */\n\tprivate onUnload(callback: () => void): void {\n\t\ttry {\n\t\t\tif (this.goveeService) {\n\t\t\t\tthis.goveeService.removeAllListeners();\n\t\t\t\tthis.goveeService.stop();\n\t\t\t}\n\t\t\t// Set connection state to false\n\t\t\tvoid this.updateStateAsync('info.connection', false);\n\t\t\tcallback();\n\t\t} catch (e: any) {\n\t\t\tthis.log.error(e.message);\n\t\t\tcallback();\n\t\t}\n\t}\n\n\t/**\n\t * Updates a state only if the value has changed.\n\t *\n\t * @param fullName Full object path\n\t * @param state New value\n\t * @param acknowledged Whether the value is acknowledged (default: true)\n\t */\n\tprivate async updateStateAsync(fullName: string, state: any, acknowledged = true): Promise<void> {\n\t\tconst currentState = await this.getStateAsync(fullName);\n\t\tif (currentState?.val !== state) {\n\t\t\tvoid this.setState(fullName, {\n\t\t\t\tval: state,\n\t\t\t\tack: acknowledged,\n\t\t\t});\n\t\t}\n\t}\n\n\t/**\n\t * Handles device discovery event from GoveeService.\n\t * Creates all necessary objects and states for the new device.\n\t *\n\t * @param event The device discovery event data.\n\t */\n\tprivate async handleDeviceDiscovered(event: DeviceDiscoveryEvent): Promise<void> {\n\t\tconst { ip, deviceName, deviceModel } = event;\n\n\t\t// Create device folder\n\t\tawait this.setObjectNotExistsAsync(deviceName, {\n\t\t\ttype: 'device',\n\t\t\tcommon: {\n\t\t\t\tname: deviceModel,\n\t\t\t\trole: 'group',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create deviceInfo folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Info',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\t// Create IP address state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.deviceInfo.ip`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'IP address of the Lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'info.ip',\n\t\t\t\tread: true,\n\t\t\t\twrite: false,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.deviceInfo.ip`, ip);\n\n\t\t// Create devStatus folder\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus`, {\n\t\t\ttype: 'folder',\n\t\t\tcommon: {\n\t\t\t\tname: 'Device Status',\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\n\t\tthis.log.info(`Device discovered: ${deviceName} at ${ip}`);\n\n\t\t// Update the list of discovered devices\n\t\tawait this.updateDiscoveredDevicesList();\n\t}\n\n\t/**\n\t * Updates the info.discoveredDevices state with the current list of all discovered devices.\n\t */\n\tprivate async updateDiscoveredDevicesList(): Promise<void> {\n\t\tconst devices = this.goveeService.getDevices();\n\t\tawait this.setStateAsync('info.discoveredDevices', {\n\t\t\tval: JSON.stringify(devices, null, 2),\n\t\t\tack: true,\n\t\t});\n\t}\n\n\t/**\n\t * Handles device status update event from GoveeService.\n\t * Creates and updates all relevant states for the device.\n\t *\n\t * @param event The device status update event data.\n\t */\n\tprivate async handleDeviceStatusUpdate(event: DeviceStatusEvent): Promise<void> {\n\t\tconst { deviceName, status } = event;\n\n\t\t// Create and update onOff state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.onOff`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'On / Off state of the lamp',\n\t\t\t\ttype: 'boolean',\n\t\t\t\trole: 'switch',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.onOff`, status.onOff);\n\n\t\t// Create and update brightness state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.brightness`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Brightness of the light',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.dimmer',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.brightness`, status.brightness);\n\n\t\t// Create and update color state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.color`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'Current showing color of the lamp',\n\t\t\t\ttype: 'string',\n\t\t\t\trole: 'level.color.rgb',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.color`, status.color);\n\n\t\t// Create and update color temperature state\n\t\tawait this.setObjectNotExistsAsync(`${deviceName}.devStatus.colorTemInKelvin`, {\n\t\t\ttype: 'state',\n\t\t\tcommon: {\n\t\t\t\tname: 'If staying in white light, the color temperature',\n\t\t\t\ttype: 'number',\n\t\t\t\trole: 'level.color.temperature',\n\t\t\t\tread: true,\n\t\t\t\twrite: true,\n\t\t\t},\n\t\t\tnative: {},\n\t\t});\n\t\tawait this.updateStateAsync(`${deviceName}.devStatus.colorTemInKelvin`, status.colorTemInKelvin);\n\t}\n}\n\n// Export factory function for ioBroker or start instance directly\nif (require.main !== module) {\n\t// Export factory function for ioBroker, also available as ES6 export\n\tmodule.exports = (options: Partial<utils.AdapterOptions> | undefined) => new GoveeLocal(options);\n} else {\n\t// Otherwise start the instance directly\n\t(() => new GoveeLocal())();\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "govee-local",
4
- "version": "0.4.2",
4
+ "version": "0.4.4",
5
5
  "news": {
6
+ "0.4.4": {
7
+ "en": "add option to run auto discovery once on startup, in interval or completly disable it",
8
+ "de": "fügen sie die option, die auto-erkennung einmal beim start zu starten, in intervall oder completly deaktivieren",
9
+ "ru": "добавить опцию запуска автоматического обнаружения один раз при запуске, в интервале или полностью отключить его",
10
+ "pt": "adicionar opção para executar a descoberta automática uma vez na inicialização, no intervalo ou desactivar completamente",
11
+ "nl": "toevoegen optie om te draaien auto discovery eenmaal bij opstarten, in interval of volledig uitschakelen",
12
+ "fr": "ajouter une option pour lancer la découverte automatique une fois au démarrage, dans l'intervalle ou la désactiver complètement",
13
+ "it": "aggiungere l'opzione per eseguire la scoperta automatica una volta all'avvio, in intervallo o completamente disattivarlo",
14
+ "es": "añadir opción para ejecutar el descubrimiento automático una vez en el inicio, en intervalo o deshabilitarlo completly",
15
+ "pl": "dodaj opcję, aby uruchomić automatyczne wykrywanie raz na starcie, w przedziale lub całkowicie go wyłączyć",
16
+ "uk": "додати можливість запустити автовідкриття один раз на запуску, в інтервалі або комплетувати його",
17
+ "zh-cn": "添加在启动、间隔或完全禁用时运行自动发现的选项"
18
+ },
19
+ "0.4.3": {
20
+ "en": "fix responsive design in the settings\nrevert forbidden characters checking\nupdate dependencies",
21
+ "de": "reagierendes design in den einstellungen beheben\nzurücksetzen verbotene zeichen überprüfen\naktualisierung der abhängigkeiten",
22
+ "ru": "исправить отзывчивый дизайн в настройках\nпроверка запрещенных персонажей\nобновление зависимостей",
23
+ "pt": "corrigir o design responsivo nas configurações\nreverter a verificação de caracteres proibidos\natualizar dependências",
24
+ "nl": "repareren responsief ontwerp in de instellingen\n_verboden tekens controleren\nafhankelijkheden bijwerken",
25
+ "fr": "fixer la conception réactive dans les paramètres\nretourner la vérification des caractères interdits\nmettre à jour les dépendances",
26
+ "it": "fissare il design reattivo nelle impostazioni\nrevert i caratteri vietati controllando\naggiornamento dipendenze",
27
+ "es": "fijar diseño sensible en la configuración\nrevertir caracteres prohibidos\ndependencias de actualización",
28
+ "pl": "naprawić reagujący projekt w ustawieniach\nrevert zabronione sprawdzanie znaków\naktualizacji zależności",
29
+ "uk": "виправити адаптивний дизайн в налаштуваннях\nперевернути заборонені символи перевірки\nоновлення залежності",
30
+ "zh-cn": "在设置中固定响应性设计\n返回禁用字符检查\n更新依赖关系"
31
+ },
6
32
  "0.4.2": {
7
33
  "en": "update dependencies\nfix device name / model identifier in the objects",
8
34
  "de": "aktualisierung der abhängigkeiten\ngerätename / modellkennung in den objekten fixieren",
@@ -43,19 +69,6 @@
43
69
  "zh-cn": "更新所有支持语言的数据点描述翻译\n更新和现代化管理 UI: 更清晰的段落、 手动 IP 地址列表、 Windows 提示和禁用自动发现选项\n通过 IP- Address和选项手动添加设备以禁用自动发现的可能性\n在信息数据点中保存自动发现的设备, 并在 admin UI 中显示"
44
70
  },
45
71
  "0.3.1": {
46
- "en": "updated release script run configuration",
47
- "de": "aktualisierte versionsskriptlaufkonfiguration",
48
- "ru": "обновленная конфигурация для запуска сценария",
49
- "pt": "configuração de execução do script de versão atualizada",
50
- "nl": "bijgewerkte versie script-run configuratie",
51
- "fr": "mise à jour de la configuration du script de publication",
52
- "it": "configurazione dell'esecuzione dello script di rilascio aggiornata",
53
- "es": "configuración actualizada script script script run",
54
- "pl": "zaktualizowana konfiguracja uruchamiania skryptu wydania",
55
- "uk": "оновлена конфігурація запуску сценарію релізу",
56
- "zh-cn": "更新发布脚本运行配置"
57
- },
58
- "0.3.0": {
59
72
  "en": "Refactored: Moved all device logic to a dedicated GoveeService class, modularized utilities\nImproved test coverage\nDependencies updated",
60
73
  "de": "Hergestellt: Alle Gerätelogik auf eine dedizierte GoveeService-Klasse verschoben, modularisierte Versorgungsprogramme\nVerbesserte Testabdeckung\nAusgaben aktualisiert",
61
74
  "ru": "Рефакторированный: Перенес всю логику устройства в выделенный класс GoveeService, модульные утилиты\nУлучшенный охват испытаниями\nОбновленные зависимости",
@@ -80,19 +93,6 @@
80
93
  "pl": "aktualizacji zależności\ndodaj obsługę węzła 24\naktualizacja do ESlint 9",
81
94
  "uk": "оновлення залежності\nдодати підтримку для вузла 24\nоновлення до ESlint 9",
82
95
  "zh-cn": "更新依赖关系\n添加节点24的支持\n更新到 Eslint 9"
83
- },
84
- "0.2.8": {
85
- "en": "update dependencies",
86
- "de": "aktualisierung der abhängigkeiten",
87
- "ru": "обновление зависимостей",
88
- "pt": "dependências",
89
- "nl": "afhankelijkheden bijwerken",
90
- "fr": "mettre à jour les dépendances",
91
- "it": "aggiornamento dipendenze",
92
- "es": "dependencias de actualización",
93
- "pl": "aktualizacji zależności",
94
- "uk": "оновлення залежності",
95
- "zh-cn": "更新依赖关系"
96
96
  }
97
97
  },
98
98
  "titleLang": {
@@ -160,6 +160,7 @@
160
160
  ]
161
161
  },
162
162
  "native": {
163
+ "scanMode": "interval",
163
164
  "searchInterval": 10,
164
165
  "deviceStatusRefreshInterval": 1,
165
166
  "extendedLogging": false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.govee-local",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "Control Govee devices via local access (no cloud)",
5
5
  "author": {
6
6
  "name": "Børge Grunicke",
@@ -31,7 +31,7 @@
31
31
  "@alcalzone/release-script-plugin-manual-review": "^4.0.0",
32
32
  "@iobroker/adapter-dev": "^1.5.0",
33
33
  "@iobroker/eslint-config": "^2.2.0",
34
- "@iobroker/testing": "^5.1.1",
34
+ "@iobroker/testing": "^5.2.2",
35
35
  "@tsconfig/node14": "^14.1.5",
36
36
  "@tsconfig/node18": "^18.2.4",
37
37
  "@tsconfig/node20": "^20.1.6",