iobroker.homewizard 0.3.1 → 0.3.3
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 +31 -7
- package/admin/i18n/de/translations.json +9 -4
- package/admin/i18n/en/translations.json +9 -4
- package/admin/i18n/es/translations.json +9 -4
- package/admin/i18n/fr/translations.json +9 -4
- package/admin/i18n/it/translations.json +9 -4
- package/admin/i18n/nl/translations.json +9 -4
- package/admin/i18n/pl/translations.json +9 -4
- package/admin/i18n/pt/translations.json +11 -6
- package/admin/i18n/ru/translations.json +9 -4
- package/admin/i18n/uk/translations.json +9 -4
- package/admin/i18n/zh-cn/translations.json +9 -4
- package/admin/jsonConfig.json +31 -4
- package/build/main.js +10 -8
- package/build/main.js.map +2 -2
- package/io-package.json +27 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,14 +51,34 @@ Real-time energy monitoring from HomeWizard devices via API v2 with WebSocket pu
|
|
|
51
51
|
|
|
52
52
|
## Configuration
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
### Prerequisites
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
The **local API** must be enabled on your HomeWizard device:
|
|
57
|
+
|
|
58
|
+
1. Open the **HomeWizard app** on your phone
|
|
59
|
+
2. Go to **Settings** > **Meters** > select your device > **Local API** > **Enable**
|
|
60
|
+
|
|
61
|
+
### Adding a device (automatic via mDNS)
|
|
62
|
+
|
|
63
|
+
1. Go to the **Objects** tab in ioBroker Admin
|
|
57
64
|
2. Set `homewizard.0.startPairing` to `true`
|
|
58
65
|
3. **Press the physical button** on your HomeWizard device within 60 seconds
|
|
59
|
-
4. The device is discovered automatically and appears
|
|
66
|
+
4. The device is discovered automatically and appears under `homewizard.0`
|
|
67
|
+
|
|
68
|
+
### Adding a device (manual IP)
|
|
69
|
+
|
|
70
|
+
If mDNS is not available (e.g. different VLAN, Docker, or firewall blocking multicast):
|
|
71
|
+
|
|
72
|
+
1. Set `homewizard.0.pairingIp` to the IP address of your device
|
|
73
|
+
2. Set `homewizard.0.startPairing` to `true`
|
|
74
|
+
3. **Press the physical button** on the device within 60 seconds
|
|
75
|
+
|
|
76
|
+
### Managing devices
|
|
77
|
+
|
|
78
|
+
All paired devices are listed in the **Objects** tab under `homewizard.0`. Each device has its own folder (e.g. `hwe-p1_5c2fafaabbcc`) with measurement data, system settings, and device info.
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
- **Remove a device:** Set its `remove` data point to `true` — the device and all data points are deleted immediately
|
|
81
|
+
- **IP changes:** Handled automatically via mDNS. No manual action needed
|
|
62
82
|
|
|
63
83
|
---
|
|
64
84
|
|
|
@@ -134,6 +154,13 @@ homewizard.0.
|
|
|
134
154
|
|
|
135
155
|
## Changelog
|
|
136
156
|
|
|
157
|
+
### 0.3.3 (2026-04-05)
|
|
158
|
+
- Fix mDNS pairing not finding devices (browser restart on pairing start)
|
|
159
|
+
- Improve log messages during pairing
|
|
160
|
+
|
|
161
|
+
### 0.3.2 (2026-04-05)
|
|
162
|
+
- Improve Admin UI and README with detailed configuration guide
|
|
163
|
+
|
|
137
164
|
### 0.3.1 (2026-04-05)
|
|
138
165
|
- Fix uncaught exception on device removal (invalid WebSocket frame during close)
|
|
139
166
|
|
|
@@ -156,9 +183,6 @@ homewizard.0.
|
|
|
156
183
|
### 0.1.1 (2026-04-04)
|
|
157
184
|
- Add unit tests (129 tests)
|
|
158
185
|
|
|
159
|
-
### 0.1.0 (2026-04-04)
|
|
160
|
-
- Initial release with API v2, WebSocket push, mDNS discovery, multi-device pairing
|
|
161
|
-
|
|
162
186
|
Older changelog: [CHANGELOG.md](CHANGELOG.md)
|
|
163
187
|
|
|
164
188
|
---
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Gerät hinzufügen",
|
|
3
|
+
"pairingAutoHeader": "Automatisch (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Gehe zum Objekte-Tab und setze 'homewizard.0.startPairing' auf true.\n2. Drücke innerhalb von 60 Sekunden den physischen Knopf am HomeWizard-Gerät.\n3. Das Gerät wird automatisch erkannt und erscheint unter dieser Adapterinstanz.",
|
|
5
|
+
"pairingManualHeader": "Manuell (feste IP)",
|
|
6
|
+
"pairingManualInfo": "Falls mDNS nicht verfügbar ist (z.B. anderes VLAN oder Docker):\n1. Setze 'homewizard.0.pairingIp' auf die IP-Adresse des Geräts.\n2. Setze dann 'homewizard.0.startPairing' auf true.\n3. Drücke innerhalb von 60 Sekunden den physischen Knopf am Gerät.",
|
|
7
|
+
"headerDevices": "Geräte verwalten",
|
|
8
|
+
"devicesInfo": "Alle gekoppelten Geräte sind im Objekte-Tab unter 'homewizard.0' aufgelistet. Jedes Gerät hat einen eigenen Ordner (z.B. hwe-p1_5c2fafaabbcc) mit Messdaten, Systemeinstellungen und Geräteinformationen.\n\nUm ein Gerät zu entfernen, setze dessen 'remove'-Datenpunkt auf true. Das Gerät und alle zugehörigen Datenpunkte werden sofort gelöscht.",
|
|
9
|
+
"headerPrerequisites": "Voraussetzungen",
|
|
10
|
+
"prerequisitesInfo": "Die lokale API muss am HomeWizard-Gerät aktiviert sein. Öffne die HomeWizard-App → Einstellungen → Messgeräte → dein Gerät → Lokale API → Aktivieren.",
|
|
6
11
|
"supportHeader": "Unterstützung",
|
|
7
12
|
"aboutInfo": "Dieser Adapter ist kostenlos und Open Source. Wenn er dir nützlich ist, erwäge die Entwicklung zu unterstützen:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Add Device",
|
|
3
|
+
"pairingAutoHeader": "Automatic (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Go to the Objects tab and set 'homewizard.0.startPairing' to true.\n2. Press the physical button on your HomeWizard device within 60 seconds.\n3. The device is discovered automatically and appears under this adapter instance.",
|
|
5
|
+
"pairingManualHeader": "Manual (fixed IP)",
|
|
6
|
+
"pairingManualInfo": "If mDNS is not available (e.g. different VLAN or Docker):\n1. Set 'homewizard.0.pairingIp' to the IP address of your device.\n2. Then set 'homewizard.0.startPairing' to true.\n3. Press the physical button on the device within 60 seconds.",
|
|
7
|
+
"headerDevices": "Manage Devices",
|
|
8
|
+
"devicesInfo": "All paired devices are listed in the Objects tab under 'homewizard.0'. Each device has its own folder (e.g. hwe-p1_5c2fafaabbcc) with measurement data, system settings, and device info.\n\nTo remove a device, set its 'remove' data point to true. The device and all its data points will be deleted immediately.",
|
|
9
|
+
"headerPrerequisites": "Prerequisites",
|
|
10
|
+
"prerequisitesInfo": "The local API must be enabled on your HomeWizard device. Open the HomeWizard app → Settings → Meters → your device → Local API → Enable.",
|
|
6
11
|
"supportHeader": "Support",
|
|
7
12
|
"aboutInfo": "This adapter is free and open source. If you find it useful, consider supporting the development:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Agregar dispositivo",
|
|
3
|
+
"pairingAutoHeader": "Automático (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Ve a la pestaña Objetos y establece 'homewizard.0.startPairing' en true.\n2. Presiona el botón físico en tu dispositivo HomeWizard dentro de 60 segundos.\n3. El dispositivo se descubre automáticamente y aparece bajo esta instancia del adaptador.",
|
|
5
|
+
"pairingManualHeader": "Manual (IP fija)",
|
|
6
|
+
"pairingManualInfo": "Si mDNS no está disponible (ej.: VLAN diferente o Docker):\n1. Establece 'homewizard.0.pairingIp' en la dirección IP del dispositivo.\n2. Luego establece 'homewizard.0.startPairing' en true.\n3. Presiona el botón físico en el dispositivo dentro de 60 segundos.",
|
|
7
|
+
"headerDevices": "Gestionar dispositivos",
|
|
8
|
+
"devicesInfo": "Todos los dispositivos emparejados están listados en la pestaña Objetos bajo 'homewizard.0'. Cada dispositivo tiene su propia carpeta (ej.: hwe-p1_5c2fafaabbcc) con datos de medición, configuraciones del sistema e información del dispositivo.\n\nPara eliminar un dispositivo, establece su punto de datos 'remove' en true. El dispositivo y todos sus puntos de datos se eliminarán inmediatamente.",
|
|
9
|
+
"headerPrerequisites": "Requisitos previos",
|
|
10
|
+
"prerequisitesInfo": "La API local debe estar activada en tu dispositivo HomeWizard. Abre la app HomeWizard → Configuración → Medidores → tu dispositivo → API Local → Activar.",
|
|
6
11
|
"supportHeader": "Apoyo",
|
|
7
12
|
"aboutInfo": "Este adaptador es gratuito y de código abierto. Si te resulta útil, considera apoyar el desarrollo:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Ajouter un appareil",
|
|
3
|
+
"pairingAutoHeader": "Automatique (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Allez dans l'onglet Objets et définissez 'homewizard.0.startPairing' sur true.\n2. Appuyez sur le bouton physique de votre appareil HomeWizard dans les 60 secondes.\n3. L'appareil est découvert automatiquement et apparaît sous cette instance d'adaptateur.",
|
|
5
|
+
"pairingManualHeader": "Manuel (IP fixe)",
|
|
6
|
+
"pairingManualInfo": "Si mDNS n'est pas disponible (ex. : VLAN différent ou Docker) :\n1. Définissez 'homewizard.0.pairingIp' sur l'adresse IP de l'appareil.\n2. Puis définissez 'homewizard.0.startPairing' sur true.\n3. Appuyez sur le bouton physique de l'appareil dans les 60 secondes.",
|
|
7
|
+
"headerDevices": "Gérer les appareils",
|
|
8
|
+
"devicesInfo": "Tous les appareils appairés sont listés dans l'onglet Objets sous 'homewizard.0'. Chaque appareil a son propre dossier (ex. : hwe-p1_5c2fafaabbcc) avec les données de mesure, les paramètres système et les informations de l'appareil.\n\nPour supprimer un appareil, définissez son point de données 'remove' sur true. L'appareil et tous ses points de données seront supprimés immédiatement.",
|
|
9
|
+
"headerPrerequisites": "Prérequis",
|
|
10
|
+
"prerequisitesInfo": "L'API locale doit être activée sur votre appareil HomeWizard. Ouvrez l'application HomeWizard → Paramètres → Compteurs → votre appareil → API Locale → Activer.",
|
|
6
11
|
"supportHeader": "Soutien",
|
|
7
12
|
"aboutInfo": "Cet adaptateur est gratuit et open source. Si vous le trouvez utile, envisagez de soutenir le développement :",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Aggiungi dispositivo",
|
|
3
|
+
"pairingAutoHeader": "Automatico (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Vai alla scheda Oggetti e imposta 'homewizard.0.startPairing' su true.\n2. Premi il pulsante fisico sul dispositivo HomeWizard entro 60 secondi.\n3. Il dispositivo viene scoperto automaticamente e appare sotto questa istanza dell'adattatore.",
|
|
5
|
+
"pairingManualHeader": "Manuale (IP fisso)",
|
|
6
|
+
"pairingManualInfo": "Se mDNS non è disponibile (es. VLAN diverso o Docker):\n1. Imposta 'homewizard.0.pairingIp' sull'indirizzo IP del dispositivo.\n2. Poi imposta 'homewizard.0.startPairing' su true.\n3. Premi il pulsante fisico sul dispositivo entro 60 secondi.",
|
|
7
|
+
"headerDevices": "Gestione dispositivi",
|
|
8
|
+
"devicesInfo": "Tutti i dispositivi associati sono elencati nella scheda Oggetti sotto 'homewizard.0'. Ogni dispositivo ha la propria cartella (es. hwe-p1_5c2fafaabbcc) con dati di misurazione, impostazioni di sistema e informazioni sul dispositivo.\n\nPer rimuovere un dispositivo, imposta il suo punto dati 'remove' su true. Il dispositivo e tutti i suoi punti dati verranno eliminati immediatamente.",
|
|
9
|
+
"headerPrerequisites": "Prerequisiti",
|
|
10
|
+
"prerequisitesInfo": "L'API locale deve essere attivata sul dispositivo HomeWizard. Apri l'app HomeWizard → Impostazioni → Contatori → il tuo dispositivo → API Locale → Attiva.",
|
|
6
11
|
"supportHeader": "Supporto",
|
|
7
12
|
"aboutInfo": "Questo adattatore è gratuito e open source. Se lo trovi utile, considera di supportare lo sviluppo:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Apparaat toevoegen",
|
|
3
|
+
"pairingAutoHeader": "Automatisch (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Ga naar het tabblad Objecten en zet 'homewizard.0.startPairing' op true.\n2. Druk binnen 60 seconden op de fysieke knop op je HomeWizard-apparaat.\n3. Het apparaat wordt automatisch gevonden en verschijnt onder deze adapterinstantie.",
|
|
5
|
+
"pairingManualHeader": "Handmatig (vast IP)",
|
|
6
|
+
"pairingManualInfo": "Als mDNS niet beschikbaar is (bijv. ander VLAN of Docker):\n1. Stel 'homewizard.0.pairingIp' in op het IP-adres van het apparaat.\n2. Zet vervolgens 'homewizard.0.startPairing' op true.\n3. Druk binnen 60 seconden op de fysieke knop op het apparaat.",
|
|
7
|
+
"headerDevices": "Apparaten beheren",
|
|
8
|
+
"devicesInfo": "Alle gekoppelde apparaten staan in het tabblad Objecten onder 'homewizard.0'. Elk apparaat heeft een eigen map (bijv. hwe-p1_5c2fafaabbcc) met meetgegevens, systeeminstellingen en apparaatinformatie.\n\nOm een apparaat te verwijderen, zet het 'remove'-datapunt op true. Het apparaat en al zijn datapunten worden onmiddellijk verwijderd.",
|
|
9
|
+
"headerPrerequisites": "Vereisten",
|
|
10
|
+
"prerequisitesInfo": "De lokale API moet ingeschakeld zijn op je HomeWizard-apparaat. Open de HomeWizard-app → Instellingen → Meters → jouw apparaat → Lokale API → Inschakelen.",
|
|
6
11
|
"supportHeader": "Ondersteuning",
|
|
7
12
|
"aboutInfo": "Deze adapter is gratis en open source. Als je het nuttig vindt, overweeg dan de ontwikkeling te steunen:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Dodaj urządzenie",
|
|
3
|
+
"pairingAutoHeader": "Automatycznie (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Przejdź do zakładki Obiekty i ustaw 'homewizard.0.startPairing' na true.\n2. Naciśnij fizyczny przycisk na urządzeniu HomeWizard w ciągu 60 sekund.\n3. Urządzenie zostanie wykryte automatycznie i pojawi się pod tą instancją adaptera.",
|
|
5
|
+
"pairingManualHeader": "Ręcznie (stały IP)",
|
|
6
|
+
"pairingManualInfo": "Jeśli mDNS nie jest dostępny (np. inny VLAN lub Docker):\n1. Ustaw 'homewizard.0.pairingIp' na adres IP urządzenia.\n2. Następnie ustaw 'homewizard.0.startPairing' na true.\n3. Naciśnij fizyczny przycisk na urządzeniu w ciągu 60 sekund.",
|
|
7
|
+
"headerDevices": "Zarządzanie urządzeniami",
|
|
8
|
+
"devicesInfo": "Wszystkie sparowane urządzenia są wymienione w zakładce Obiekty pod 'homewizard.0'. Każde urządzenie ma własny folder (np. hwe-p1_5c2fafaabbcc) z danymi pomiarowymi, ustawieniami systemowymi i informacjami o urządzeniu.\n\nAby usunąć urządzenie, ustaw jego punkt danych 'remove' na true. Urządzenie i wszystkie jego punkty danych zostaną natychmiast usunięte.",
|
|
9
|
+
"headerPrerequisites": "Wymagania",
|
|
10
|
+
"prerequisitesInfo": "Lokalne API musi być włączone na urządzeniu HomeWizard. Otwórz aplikację HomeWizard → Ustawienia → Liczniki → twoje urządzenie → Lokalne API → Włącz.",
|
|
6
11
|
"supportHeader": "Wsparcie",
|
|
7
12
|
"aboutInfo": "Ten adapter jest darmowy i otwartoźródłowy. Jeśli uważasz go za przydatny, rozważ wsparcie rozwoju:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
"
|
|
7
|
-
"
|
|
2
|
+
"headerPairing": "Adicionar dispositivo",
|
|
3
|
+
"pairingAutoHeader": "Automático (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Vá para a aba Objetos e defina 'homewizard.0.startPairing' como true.\n2. Pressione o botão físico no dispositivo HomeWizard dentro de 60 segundos.\n3. O dispositivo é descoberto automaticamente e aparece sob esta instância do adaptador.",
|
|
5
|
+
"pairingManualHeader": "Manual (IP fixo)",
|
|
6
|
+
"pairingManualInfo": "Se o mDNS não estiver disponível (ex.: VLAN diferente ou Docker):\n1. Defina 'homewizard.0.pairingIp' com o endereço IP do dispositivo.\n2. Depois defina 'homewizard.0.startPairing' como true.\n3. Pressione o botão físico no dispositivo dentro de 60 segundos.",
|
|
7
|
+
"headerDevices": "Gerenciar dispositivos",
|
|
8
|
+
"devicesInfo": "Todos os dispositivos emparelhados estão listados na aba Objetos sob 'homewizard.0'. Cada dispositivo tem sua própria pasta (ex.: hwe-p1_5c2fafaabbcc) com dados de medição, configurações do sistema e informações do dispositivo.\n\nPara remover um dispositivo, defina seu ponto de dados 'remove' como true. O dispositivo e todos os seus pontos de dados serão excluídos imediatamente.",
|
|
9
|
+
"headerPrerequisites": "Pré-requisitos",
|
|
10
|
+
"prerequisitesInfo": "A API local deve estar ativada no dispositivo HomeWizard. Abra o app HomeWizard → Configurações → Medidores → seu dispositivo → API Local → Ativar.",
|
|
11
|
+
"supportHeader": "Suporte",
|
|
12
|
+
"aboutInfo": "Este adaptador é gratuito e de código aberto. Se for útil para você, considere apoiar o desenvolvimento:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
9
14
|
"donatePaypal": "PayPal"
|
|
10
15
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Добавить устройство",
|
|
3
|
+
"pairingAutoHeader": "Автоматически (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Перейдите на вкладку Объекты и установите 'homewizard.0.startPairing' в true.\n2. Нажмите физическую кнопку на устройстве HomeWizard в течение 60 секунд.\n3. Устройство будет обнаружено автоматически и появится под этим экземпляром адаптера.",
|
|
5
|
+
"pairingManualHeader": "Вручную (фиксированный IP)",
|
|
6
|
+
"pairingManualInfo": "Если mDNS недоступен (например, другой VLAN или Docker):\n1. Установите 'homewizard.0.pairingIp' на IP-адрес устройства.\n2. Затем установите 'homewizard.0.startPairing' в true.\n3. Нажмите физическую кнопку на устройстве в течение 60 секунд.",
|
|
7
|
+
"headerDevices": "Управление устройствами",
|
|
8
|
+
"devicesInfo": "Все сопряжённые устройства перечислены на вкладке Объекты под 'homewizard.0'. Каждое устройство имеет свою папку (например, hwe-p1_5c2fafaabbcc) с данными измерений, системными настройками и информацией об устройстве.\n\nЧтобы удалить устройство, установите его точку данных 'remove' в true. Устройство и все его точки данных будут немедленно удалены.",
|
|
9
|
+
"headerPrerequisites": "Предварительные требования",
|
|
10
|
+
"prerequisitesInfo": "Локальный API должен быть включён на устройстве HomeWizard. Откройте приложение HomeWizard → Настройки → Счётчики → ваше устройство → Локальный API → Включить.",
|
|
6
11
|
"supportHeader": "Поддержка",
|
|
7
12
|
"aboutInfo": "Этот адаптер бесплатный и с открытым исходным кодом. Если он вам полезен, рассмотрите поддержку разработки:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "Додати пристрій",
|
|
3
|
+
"pairingAutoHeader": "Автоматично (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. Перейдіть на вкладку Об'єкти і встановіть 'homewizard.0.startPairing' на true.\n2. Натисніть фізичну кнопку на пристрої HomeWizard протягом 60 секунд.\n3. Пристрій буде виявлено автоматично і з'явиться під цим екземпляром адаптера.",
|
|
5
|
+
"pairingManualHeader": "Вручну (фіксований IP)",
|
|
6
|
+
"pairingManualInfo": "Якщо mDNS недоступний (напр. інший VLAN або Docker):\n1. Встановіть 'homewizard.0.pairingIp' на IP-адресу пристрою.\n2. Потім встановіть 'homewizard.0.startPairing' на true.\n3. Натисніть фізичну кнопку на пристрої протягом 60 секунд.",
|
|
7
|
+
"headerDevices": "Керування пристроями",
|
|
8
|
+
"devicesInfo": "Всі сполучені пристрої перелічені на вкладці Об'єкти під 'homewizard.0'. Кожен пристрій має власну папку (напр. hwe-p1_5c2fafaabbcc) з даними вимірювань, системними налаштуваннями та інформацією про пристрій.\n\nЩоб видалити пристрій, встановіть його точку даних 'remove' на true. Пристрій та всі його точки даних будуть негайно видалені.",
|
|
9
|
+
"headerPrerequisites": "Передумови",
|
|
10
|
+
"prerequisitesInfo": "Локальний API повинен бути увімкнений на пристрої HomeWizard. Відкрийте додаток HomeWizard → Налаштування → Лічильники → ваш пристрій → Локальний API → Увімкнути.",
|
|
6
11
|
"supportHeader": "Підтримка",
|
|
7
12
|
"aboutInfo": "Цей адаптер безкоштовний і з відкритим кодом. Якщо він вам корисний, розгляньте підтримку розробки:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
{
|
|
2
|
-
"headerPairing": "
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"
|
|
2
|
+
"headerPairing": "添加设备",
|
|
3
|
+
"pairingAutoHeader": "自动 (mDNS)",
|
|
4
|
+
"pairingAutoInfo": "1. 转到对象选项卡,将 'homewizard.0.startPairing' 设置为 true。\n2. 在 60 秒内按下 HomeWizard 设备上的物理按钮。\n3. 设备将被自动发现并显示在此适配器实例下。",
|
|
5
|
+
"pairingManualHeader": "手动 (固定 IP)",
|
|
6
|
+
"pairingManualInfo": "如果 mDNS 不可用(如不同 VLAN 或 Docker):\n1. 将 'homewizard.0.pairingIp' 设置为设备的 IP 地址。\n2. 然后将 'homewizard.0.startPairing' 设置为 true。\n3. 在 60 秒内按下设备上的物理按钮。",
|
|
7
|
+
"headerDevices": "管理设备",
|
|
8
|
+
"devicesInfo": "所有已配对的设备都列在对象选项卡中的 'homewizard.0' 下。每个设备都有自己的文件夹(如 hwe-p1_5c2fafaabbcc),包含测量数据、系统设置和设备信息。\n\n要移除设备,请将其 'remove' 数据点设置为 true。设备及其所有数据点将被立即删除。",
|
|
9
|
+
"headerPrerequisites": "前提条件",
|
|
10
|
+
"prerequisitesInfo": "必须在 HomeWizard 设备上启用本地 API。打开 HomeWizard 应用 → 设置 → 仪表 → 您的设备 → 本地 API → 启用。",
|
|
6
11
|
"supportHeader": "支持",
|
|
7
12
|
"aboutInfo": "此适配器免费且开源。如果您觉得有用,请考虑支持开发:",
|
|
8
13
|
"donateKofi": "Ko-fi",
|
package/admin/jsonConfig.json
CHANGED
|
@@ -2,16 +2,43 @@
|
|
|
2
2
|
"i18n": true,
|
|
3
3
|
"type": "panel",
|
|
4
4
|
"items": {
|
|
5
|
+
"_headerPrerequisites": {
|
|
6
|
+
"type": "header",
|
|
7
|
+
"text": "headerPrerequisites",
|
|
8
|
+
"size": 3
|
|
9
|
+
},
|
|
10
|
+
"_prerequisitesInfo": {
|
|
11
|
+
"type": "staticText",
|
|
12
|
+
"text": "prerequisitesInfo",
|
|
13
|
+
"xs": 12, "sm": 12, "md": 12, "lg": 12, "xl": 12,
|
|
14
|
+
"style": { "fontSize": 14, "marginBottom": 16 }
|
|
15
|
+
},
|
|
5
16
|
"_headerPairing": {
|
|
6
17
|
"type": "header",
|
|
7
18
|
"text": "headerPairing",
|
|
8
19
|
"size": 3
|
|
9
20
|
},
|
|
10
|
-
"
|
|
21
|
+
"_pairingAutoHeader": {
|
|
22
|
+
"type": "header",
|
|
23
|
+
"text": "pairingAutoHeader",
|
|
24
|
+
"size": 5
|
|
25
|
+
},
|
|
26
|
+
"_pairingAutoInfo": {
|
|
11
27
|
"type": "staticText",
|
|
12
|
-
"text": "
|
|
28
|
+
"text": "pairingAutoInfo",
|
|
13
29
|
"xs": 12, "sm": 12, "md": 12, "lg": 12, "xl": 12,
|
|
14
|
-
"style": { "fontSize": 14, "marginBottom": 16 }
|
|
30
|
+
"style": { "fontSize": 14, "marginBottom": 16, "whiteSpace": "pre-line" }
|
|
31
|
+
},
|
|
32
|
+
"_pairingManualHeader": {
|
|
33
|
+
"type": "header",
|
|
34
|
+
"text": "pairingManualHeader",
|
|
35
|
+
"size": 5
|
|
36
|
+
},
|
|
37
|
+
"_pairingManualInfo": {
|
|
38
|
+
"type": "staticText",
|
|
39
|
+
"text": "pairingManualInfo",
|
|
40
|
+
"xs": 12, "sm": 12, "md": 12, "lg": 12, "xl": 12,
|
|
41
|
+
"style": { "fontSize": 14, "marginBottom": 16, "whiteSpace": "pre-line" }
|
|
15
42
|
},
|
|
16
43
|
"_headerDevices": {
|
|
17
44
|
"type": "header",
|
|
@@ -22,7 +49,7 @@
|
|
|
22
49
|
"type": "staticText",
|
|
23
50
|
"text": "devicesInfo",
|
|
24
51
|
"xs": 12, "sm": 12, "md": 12, "lg": 12, "xl": 12,
|
|
25
|
-
"style": { "fontSize": 14, "marginBottom": 16 }
|
|
52
|
+
"style": { "fontSize": 14, "marginBottom": 16, "whiteSpace": "pre-line" }
|
|
26
53
|
},
|
|
27
54
|
"_supportHeader": {
|
|
28
55
|
"newLine": true,
|
package/build/main.js
CHANGED
|
@@ -197,7 +197,7 @@ class HomeWizard extends utils.Adapter {
|
|
|
197
197
|
)) {
|
|
198
198
|
this.discoveredDuringPairing.push(discovered);
|
|
199
199
|
this.log.info(
|
|
200
|
-
`
|
|
200
|
+
`Found ${discovered.name} (${discovered.productType}) at ${discovered.ip} \u2014 press the button on the device to pair`
|
|
201
201
|
);
|
|
202
202
|
}
|
|
203
203
|
return;
|
|
@@ -345,7 +345,7 @@ class HomeWizard extends utils.Adapter {
|
|
|
345
345
|
await this.setStateAsync("pairingIp", { val: "", ack: true });
|
|
346
346
|
if (this.pairingManualIp) {
|
|
347
347
|
this.log.info(
|
|
348
|
-
`Pairing
|
|
348
|
+
`Pairing mode enabled for ${this.pairingManualIp} \u2014 press the button on your HomeWizard device now (60 seconds timeout)`
|
|
349
349
|
);
|
|
350
350
|
this.discoveredDuringPairing.push({
|
|
351
351
|
ip: this.pairingManualIp,
|
|
@@ -355,21 +355,23 @@ class HomeWizard extends utils.Adapter {
|
|
|
355
355
|
});
|
|
356
356
|
} else {
|
|
357
357
|
this.log.info(
|
|
358
|
-
"Pairing mode
|
|
358
|
+
"Pairing mode enabled \u2014 searching for devices via mDNS, press the button on your HomeWizard device now (60 seconds timeout)"
|
|
359
359
|
);
|
|
360
360
|
if (!this.discovery) {
|
|
361
361
|
this.discovery = new import_discovery.HomeWizardDiscovery(this.log);
|
|
362
|
-
this.discovery.start((discovered) => {
|
|
363
|
-
this.onDeviceDiscovered(discovered);
|
|
364
|
-
});
|
|
365
362
|
}
|
|
363
|
+
this.discovery.start((discovered) => {
|
|
364
|
+
this.onDeviceDiscovered(discovered);
|
|
365
|
+
});
|
|
366
366
|
}
|
|
367
367
|
this.pairingPollTimer = this.setInterval(() => {
|
|
368
368
|
void this.pollPairing();
|
|
369
369
|
}, PAIRING_POLL_MS);
|
|
370
370
|
this.pairingTimer = this.setTimeout(() => {
|
|
371
371
|
this.stopPairing();
|
|
372
|
-
this.log.info(
|
|
372
|
+
this.log.info(
|
|
373
|
+
"Pairing mode automatically disabled after 60 seconds timeout"
|
|
374
|
+
);
|
|
373
375
|
}, PAIRING_TIMEOUT_MS);
|
|
374
376
|
}
|
|
375
377
|
/** Poll all discovered devices to attempt pairing */
|
|
@@ -379,7 +381,7 @@ class HomeWizard extends utils.Adapter {
|
|
|
379
381
|
const client = new import_homewizard_client.HomeWizardClient(device.ip);
|
|
380
382
|
const result = await client.requestPairing();
|
|
381
383
|
this.log.info(
|
|
382
|
-
`
|
|
384
|
+
`Successfully paired with ${device.name} (${device.productType}) at ${device.ip} \u2014 connecting...`
|
|
383
385
|
);
|
|
384
386
|
const authedClient = new import_homewizard_client.HomeWizardClient(device.ip, result.token);
|
|
385
387
|
const info = await authedClient.getDeviceInfo();
|
package/build/main.js.map
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../src/main.ts"],
|
|
4
|
-
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { HomeWizardDiscovery } from \"./lib/discovery\";\nimport { HomeWizardApiError, HomeWizardClient } from \"./lib/homewizard-client\";\nimport { StateManager } from \"./lib/state-manager\";\nimport type {\n DeviceConfig,\n DeviceConnection,\n DiscoveredDevice,\n Measurement,\n} from \"./lib/types\";\nimport { HomeWizardWebSocket } from \"./lib/websocket-client\";\n\n/** Pairing timeout in milliseconds (60 seconds) */\nconst PAIRING_TIMEOUT_MS = 60_000;\n/** Pairing poll interval in milliseconds */\nconst PAIRING_POLL_MS = 2_000;\n/** WebSocket reconnect base delay in milliseconds */\nconst WS_RECONNECT_BASE_MS = 5_000;\n/** Maximum WebSocket reconnect delay in milliseconds */\nconst WS_RECONNECT_MAX_MS = 300_000;\n/** REST fallback poll interval in milliseconds */\nconst REST_POLL_MS = 10_000;\n/** System info poll interval in milliseconds */\nconst SYSTEM_POLL_MS = 60_000;\n/** Max auth failures before giving up */\nconst MAX_AUTH_FAILURES = 3;\n\nclass HomeWizard extends utils.Adapter {\n private stateManager!: StateManager;\n private discovery: HomeWizardDiscovery | null = null;\n private readonly connections = new Map<string, DeviceConnection>();\n private pairingTimer: ioBroker.Timeout | undefined = undefined;\n private pairingPollTimer: ioBroker.Interval | undefined = undefined;\n private systemPollTimer: ioBroker.Interval | undefined = undefined;\n private isPairing = false;\n private pairingManualIp = \"\";\n private discoveredDuringPairing: DiscoveredDevice[] = [];\n\n /** @param options Adapter options */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({ ...options, name: \"homewizard\" });\n this.on(\"ready\", () => this.onReady());\n this.on(\"stateChange\", (id, state) => this.onStateChange(id, state));\n this.on(\"unload\", (callback) => this.onUnload(callback));\n }\n\n /** Adapter started */\n private async onReady(): Promise<void> {\n this.stateManager = new StateManager(this);\n\n // Create pairing states\n await this.extendObjectAsync(\"pairingIp\", {\n type: \"state\",\n common: {\n name: \"Device IP for manual pairing\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: true,\n def: \"\",\n },\n native: {},\n });\n\n // Reset pairing states on start (in case previous run was killed mid-pairing)\n await this.setStateAsync(\"startPairing\", { val: false, ack: true });\n await this.setStateAsync(\"pairingIp\", { val: \"\", ack: true });\n\n // Subscribe to pairing button and writable device states\n await this.subscribeStatesAsync(\"startPairing\");\n await this.subscribeStatesAsync(\"*.system.reboot\");\n await this.subscribeStatesAsync(\"*.system.identify\");\n await this.subscribeStatesAsync(\"*.system.cloud_enabled\");\n await this.subscribeStatesAsync(\"*.system.status_led_brightness_pct\");\n await this.subscribeStatesAsync(\"*.system.api_v1_enabled\");\n await this.subscribeStatesAsync(\"*.battery.mode\");\n await this.subscribeStatesAsync(\"*.battery.permissions\");\n await this.subscribeStatesAsync(\"*.remove\");\n\n // Load devices from device objects (not from adapter config)\n const devices = await this.loadDevicesFromObjects();\n if (devices.length === 0) {\n this.log.info(\n \"No devices configured \u2014 press 'Start Pairing' to add a HomeWizard device\",\n );\n await this.setStateAsync(\"info.connection\", { val: false, ack: true });\n }\n\n // Create connection entries for all configured devices\n for (const device of devices) {\n const key = this.stateManager.devicePrefix(device);\n await this.stateManager.createDeviceStates(device);\n const conn: DeviceConnection = {\n config: device,\n ip: device.ip || \"\",\n wsClient: null,\n wsAuthenticated: false,\n pollTimer: undefined,\n reconnectTimer: undefined,\n wsFailCount: 0,\n authFailCount: 0,\n lastErrorCode: \"\",\n };\n this.connections.set(key, conn);\n\n // If we have a stored IP, connect immediately\n if (conn.ip) {\n this.log.debug(`Using stored IP ${conn.ip} for ${device.productName}`);\n void this.initDevice(conn);\n }\n }\n\n // Start mDNS discovery (updates IP if device announces)\n this.discovery = new HomeWizardDiscovery(this.log);\n this.discovery.start((discovered) => {\n this.onDeviceDiscovered(discovered);\n });\n\n // Periodic system info poll\n this.systemPollTimer = this.setInterval(() => {\n void this.pollAllSystemInfo();\n }, SYSTEM_POLL_MS);\n\n this.updateGlobalConnection();\n }\n\n /**\n * Load device configs from existing device objects\n * Tokens are stored encrypted in device object native\n */\n private async loadDevicesFromObjects(): Promise<DeviceConfig[]> {\n const devices: DeviceConfig[] = [];\n\n // Also migrate from old adapter config if devices exist there\n const oldDevices: DeviceConfig[] =\n ((this.config as Record<string, unknown>).devices as DeviceConfig[]) ||\n [];\n if (oldDevices.length > 0) {\n this.log.debug(\n `Migrating ${oldDevices.length} device(s) from adapter config to device objects`,\n );\n for (const device of oldDevices) {\n await this.saveDeviceToObject(device);\n }\n // Clear old config (this triggers one restart, but only during migration)\n await this.extendForeignObjectAsync(`system.adapter.${this.namespace}`, {\n native: { devices: [] },\n });\n return oldDevices;\n }\n\n // Read device objects from our namespace\n const objects = await this.getAdapterObjectsAsync();\n for (const [id, obj] of Object.entries(objects)) {\n if (obj.type !== \"device\") {\n continue;\n }\n const native = obj.native as Record<string, string> | undefined;\n if (!native?.encryptedToken || !native.serial) {\n continue;\n }\n const localId = id.replace(`${this.namespace}.`, \"\");\n this.log.debug(`Loading device from object: ${localId}`);\n const token = this.decrypt(native.encryptedToken);\n devices.push({\n token,\n productType: native.productType || \"unknown\",\n serial: native.serial,\n productName: native.productName || native.productType || \"unknown\",\n ...(native.ip ? { ip: native.ip } : {}),\n });\n }\n\n return devices;\n }\n\n /**\n * Save device config to its device object native (encrypted token)\n *\n * @param config Device configuration to save\n */\n private async saveDeviceToObject(config: DeviceConfig): Promise<void> {\n const prefix = this.stateManager.devicePrefix(config);\n const encryptedToken = this.encrypt(config.token);\n await this.extendObjectAsync(prefix, {\n type: \"device\",\n common: { name: config.productName || config.productType },\n native: {\n encryptedToken,\n productType: config.productType,\n serial: config.serial,\n productName: config.productName,\n ...(config.ip ? { ip: config.ip } : {}),\n },\n });\n }\n\n /**\n * Remove device config from its device object\n *\n * @param config Device configuration to remove\n */\n private async removeDeviceFromObject(config: DeviceConfig): Promise<void> {\n await this.stateManager.removeDevice(config);\n }\n\n /**\n * Handle a discovered device from mDNS\n *\n * @param discovered Discovered device info\n */\n private onDeviceDiscovered(discovered: DiscoveredDevice): void {\n // During pairing, collect new devices\n if (this.isPairing) {\n const existing = Array.from(this.connections.values()).find(\n (c) => c.config.serial === discovered.serial,\n );\n if (!existing) {\n if (\n !this.discoveredDuringPairing.find(\n (d) => d.serial === discovered.serial,\n )\n ) {\n this.discoveredDuringPairing.push(discovered);\n this.log.info(\n `Discovered: ${discovered.name} (${discovered.productType}) at ${discovered.ip} \u2014 waiting for button press...`,\n );\n }\n return;\n }\n }\n\n // Match against configured devices\n for (const [, conn] of this.connections) {\n if (conn.config.serial !== discovered.serial) {\n continue;\n }\n\n // Update IP if changed\n if (conn.ip !== discovered.ip) {\n const oldIp = conn.ip;\n conn.ip = discovered.ip;\n\n if (oldIp) {\n this.log.debug(\n `${conn.config.productName}: IP changed ${oldIp} \u2192 ${discovered.ip}`,\n );\n // Close active WS so it reconnects with new IP\n if (conn.wsClient) {\n conn.wsClient.close();\n conn.wsClient = null;\n conn.wsAuthenticated = false;\n // Cancel pending reconnect \u2014 we reconnect immediately\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n conn.reconnectTimer = undefined;\n }\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n conn.pollTimer = undefined;\n }\n conn.wsFailCount = 0;\n conn.authFailCount = 0;\n this.connectWebSocket(conn);\n }\n }\n }\n\n // Connect if not already connected\n if (!conn.wsClient && !conn.reconnectTimer) {\n this.log.debug(\n `mDNS: found ${conn.config.productName} at ${discovered.ip}`,\n );\n void this.initDevice(conn);\n }\n\n return;\n }\n }\n\n /**\n * Adapter stopping \u2014 MUST be synchronous\n *\n * @param callback Completion callback\n */\n private onUnload(callback: () => void): void {\n if (this.pairingTimer) {\n this.clearTimeout(this.pairingTimer);\n }\n if (this.pairingPollTimer) {\n this.clearInterval(this.pairingPollTimer);\n }\n if (this.systemPollTimer) {\n this.clearInterval(this.systemPollTimer);\n }\n\n this.discovery?.stop();\n\n for (const conn of this.connections.values()) {\n conn.wsClient?.close();\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n }\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n }\n }\n this.connections.clear();\n\n void this.setState(\"info.connection\", { val: false, ack: true });\n callback();\n }\n\n /**\n * Handle state changes\n *\n * @param id State ID\n * @param state State value\n */\n private async onStateChange(\n id: string,\n state: ioBroker.State | null | undefined,\n ): Promise<void> {\n if (!state || state.ack) {\n return;\n }\n\n // Pairing button\n if (id.endsWith(\".startPairing\")) {\n if (state.val) {\n await this.startPairing();\n }\n return;\n }\n\n // Remove device button\n if (id.endsWith(\".remove\")) {\n if (state.val) {\n await this.removeDevice(id);\n }\n return;\n }\n\n // Find which device this state belongs to\n const conn = this.findConnectionForState(id);\n if (!conn || !conn.ip) {\n return;\n }\n\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n\n try {\n if (id.endsWith(\".system.reboot\")) {\n this.log.info(`Rebooting ${conn.config.productName} (${conn.ip})`);\n await client.reboot();\n } else if (id.endsWith(\".system.identify\")) {\n await client.identify();\n } else if (id.endsWith(\".system.cloud_enabled\")) {\n await client.setSystem({ cloud_enabled: !!state.val });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".system.status_led_brightness_pct\")) {\n await client.setSystem({\n status_led_brightness_pct: Number(state.val),\n });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".system.api_v1_enabled\")) {\n await client.setSystem({ api_v1_enabled: !!state.val });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".battery.mode\")) {\n await client.setBatteries({\n mode: String(state.val) as \"zero\" | \"to_full\" | \"standby\",\n });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".battery.permissions\")) {\n const perms = JSON.parse(String(state.val));\n await client.setBatteries({ permissions: perms });\n await this.setStateAsync(id, { val: state.val, ack: true });\n }\n } catch (err) {\n this.log.warn(\n `Failed to set ${id}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /** Start pairing mode \u2014 discover devices and attempt to pair */\n private async startPairing(): Promise<void> {\n if (this.isPairing) {\n this.log.debug(\"Pairing already active\");\n return;\n }\n\n // Reset startPairing immediately so it doesn't survive a restart\n await this.setStateAsync(\"startPairing\", { val: false, ack: true });\n\n this.isPairing = true;\n this.discoveredDuringPairing = [];\n\n // Check if manual IP is set, then clear pairingIp immediately\n const ipState = await this.getStateAsync(\"pairingIp\");\n this.pairingManualIp = ipState?.val ? String(ipState.val).trim() : \"\";\n await this.setStateAsync(\"pairingIp\", { val: \"\", ack: true });\n\n if (this.pairingManualIp) {\n this.log.info(\n `Pairing with manual IP ${this.pairingManualIp} \u2014 press the button on your HomeWizard device within 60 seconds!`,\n );\n // Add as discovered device immediately\n this.discoveredDuringPairing.push({\n ip: this.pairingManualIp,\n productType: \"unknown\",\n serial: \"unknown\",\n name: this.pairingManualIp,\n });\n } else {\n this.log.info(\n \"Pairing mode started (mDNS) \u2014 press the button on your HomeWizard device within 60 seconds!\",\n );\n // If discovery isn't running yet (no devices configured), start it\n if (!this.discovery) {\n this.discovery = new HomeWizardDiscovery(this.log);\n this.discovery.start((discovered) => {\n this.onDeviceDiscovered(discovered);\n });\n }\n }\n\n // Poll discovered devices for pairing\n this.pairingPollTimer = this.setInterval(() => {\n void this.pollPairing();\n }, PAIRING_POLL_MS);\n\n // Timeout pairing\n this.pairingTimer = this.setTimeout(() => {\n this.stopPairing();\n this.log.info(\"Pairing mode timed out\");\n }, PAIRING_TIMEOUT_MS);\n }\n\n /** Poll all discovered devices to attempt pairing */\n private async pollPairing(): Promise<void> {\n for (const device of this.discoveredDuringPairing) {\n try {\n const client = new HomeWizardClient(device.ip);\n const result = await client.requestPairing();\n\n // Success! Button was pressed\n this.log.info(\n `Paired with ${device.name} (${device.productType}) at ${device.ip}`,\n );\n\n // Get device info\n const authedClient = new HomeWizardClient(device.ip, result.token);\n const info = await authedClient.getDeviceInfo();\n\n const deviceConfig: DeviceConfig = {\n token: result.token,\n productType: info.product_type,\n serial: info.serial,\n productName: info.product_name,\n ...(this.pairingManualIp ? { ip: this.pairingManualIp } : {}),\n };\n\n // Save to device object (no adapter restart!)\n await this.saveDeviceToObject(deviceConfig);\n await this.stateManager.createDeviceStates(deviceConfig);\n\n // Create connection and connect\n const key = this.stateManager.devicePrefix(deviceConfig);\n const conn: DeviceConnection = {\n config: deviceConfig,\n ip: device.ip,\n wsClient: null,\n wsAuthenticated: false,\n pollTimer: undefined,\n reconnectTimer: undefined,\n wsFailCount: 0,\n authFailCount: 0,\n lastErrorCode: \"\",\n };\n this.connections.set(key, conn);\n void this.initDevice(conn);\n\n // Remove from discovery list\n this.discoveredDuringPairing = this.discoveredDuringPairing.filter(\n (d) => d.serial !== info.serial,\n );\n\n this.stopPairing();\n this.updateGlobalConnection();\n return;\n } catch (err) {\n // 403 = button not pressed yet \u2014 expected, keep polling\n if (err instanceof HomeWizardApiError && err.statusCode === 403) {\n continue;\n }\n this.log.debug(\n `Pairing poll error for ${device.ip}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n /** Stop pairing mode */\n private stopPairing(): void {\n this.isPairing = false;\n this.pairingManualIp = \"\";\n this.discoveredDuringPairing = [];\n\n if (this.pairingPollTimer) {\n this.clearInterval(this.pairingPollTimer);\n this.pairingPollTimer = undefined;\n }\n if (this.pairingTimer) {\n this.clearTimeout(this.pairingTimer);\n this.pairingTimer = undefined;\n }\n }\n\n /**\n * Initialize a newly discovered device \u2014 fetch info and connect WebSocket\n *\n * @param conn Device connection with IP set\n */\n private async initDevice(conn: DeviceConnection): Promise<void> {\n try {\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n const info = await client.getDeviceInfo();\n const key = this.stateManager.devicePrefix(conn.config);\n await this.setStateAsync(`${key}.info.firmware`, {\n val: info.firmware_version,\n ack: true,\n });\n } catch (err) {\n this.logDeviceError(conn, \"init\", err);\n }\n\n this.connectWebSocket(conn);\n void this.pollSystemInfo(conn);\n }\n\n /**\n * Connect WebSocket for a device\n *\n * @param conn Device connection\n */\n private connectWebSocket(conn: DeviceConnection): void {\n if (!conn.ip) {\n return; // No IP yet \u2014 wait for mDNS\n }\n\n // Stop reconnecting if auth keeps failing\n if (conn.authFailCount >= MAX_AUTH_FAILURES) {\n return;\n }\n\n const key = this.stateManager.devicePrefix(conn.config);\n\n const wsClient = new HomeWizardWebSocket(conn.ip, conn.config.token, {\n onMeasurement: (data: Measurement) => {\n void this.stateManager.updateMeasurement(conn.config, data);\n },\n onConnected: () => {\n conn.wsAuthenticated = true;\n conn.wsFailCount = 0;\n conn.authFailCount = 0;\n void this.stateManager.setDeviceConnected(conn.config, true);\n this.updateGlobalConnection();\n\n // Stop REST fallback if active\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n conn.pollTimer = undefined;\n }\n\n // Log restoration if we had errors before\n if (conn.lastErrorCode) {\n this.log.info(`${conn.config.productName}: connection restored`);\n conn.lastErrorCode = \"\";\n }\n\n this.log.debug(\n `WebSocket connected to ${conn.config.productName} (${conn.ip})`,\n );\n },\n onDisconnected: (error?: Error) => {\n conn.wsAuthenticated = false;\n conn.wsClient = null;\n void this.stateManager.setDeviceConnected(conn.config, false);\n this.updateGlobalConnection();\n\n if (error) {\n this.logDeviceError(conn, \"ws\", error);\n }\n\n // Check if this was an auth failure\n if (\n error instanceof HomeWizardApiError &&\n error.errorCode === \"user:unauthorized\"\n ) {\n conn.authFailCount++;\n if (conn.authFailCount >= MAX_AUTH_FAILURES) {\n this.log.warn(\n `${conn.config.productName}: token invalid \u2014 re-pair device to fix`,\n );\n return;\n }\n }\n\n // Start REST fallback\n this.startRestFallback(conn);\n\n // Schedule reconnect with exponential backoff\n conn.wsFailCount++;\n const delay = Math.min(\n WS_RECONNECT_BASE_MS * Math.pow(2, conn.wsFailCount - 1),\n WS_RECONNECT_MAX_MS,\n );\n this.log.debug(\n `${key}: WS reconnect in ${delay / 1000}s (attempt ${conn.wsFailCount})`,\n );\n\n conn.reconnectTimer = this.setTimeout(() => {\n conn.reconnectTimer = undefined;\n this.connectWebSocket(conn);\n }, delay);\n },\n log: this.log,\n });\n\n conn.wsClient = wsClient;\n wsClient.connect();\n }\n\n /**\n * Start REST polling as fallback when WebSocket is down\n *\n * @param conn Device connection\n */\n private startRestFallback(conn: DeviceConnection): void {\n if (conn.pollTimer || !conn.ip) {\n return;\n }\n\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n\n conn.pollTimer = this.setInterval(async () => {\n try {\n const data = await client.getMeasurement();\n await this.stateManager.updateMeasurement(conn.config, data);\n await this.stateManager.setDeviceConnected(conn.config, true);\n } catch (err) {\n this.logDeviceError(conn, \"rest\", err);\n await this.stateManager.setDeviceConnected(conn.config, false);\n }\n this.updateGlobalConnection();\n }, REST_POLL_MS);\n }\n\n /** Poll system info for all connected devices */\n private async pollAllSystemInfo(): Promise<void> {\n for (const conn of this.connections.values()) {\n // Only poll devices that have an IP and are connected or at least reachable\n if (conn.ip && (conn.wsAuthenticated || conn.pollTimer)) {\n await this.pollSystemInfo(conn);\n }\n }\n }\n\n /**\n * Poll system info for a single device\n *\n * @param conn Device connection\n */\n private async pollSystemInfo(conn: DeviceConnection): Promise<void> {\n if (!conn.ip) {\n return;\n }\n\n try {\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n const system = await client.getSystem();\n await this.stateManager.updateSystem(conn.config, system);\n\n // Also poll battery if device supports it\n try {\n const battery = await client.getBatteries();\n // Only create battery states if batteries are actually connected\n if (battery.battery_count && battery.battery_count > 0) {\n await this.stateManager.updateBattery(conn.config, battery);\n }\n } catch {\n // Device may not support batteries \u2014 that's fine\n }\n } catch (err) {\n this.logDeviceError(conn, \"system\", err);\n }\n }\n\n /** Update global info.connection based on all device states */\n private updateGlobalConnection(): void {\n const anyConnected = Array.from(this.connections.values()).some(\n (c) => c.wsAuthenticated,\n );\n void this.setStateAsync(\"info.connection\", {\n val: anyConnected,\n ack: true,\n });\n }\n\n /**\n * Remove a device \u2014 disconnect, delete states and object\n *\n * @param stateId The remove state ID\n */\n private async removeDevice(stateId: string): Promise<void> {\n const conn = this.findConnectionForState(stateId);\n if (!conn) {\n return;\n }\n\n const key = this.stateManager.devicePrefix(conn.config);\n this.log.info(\n `Removing device ${conn.config.productName} (${conn.config.serial})`,\n );\n\n // Disconnect\n conn.wsClient?.close();\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n }\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n }\n this.connections.delete(key);\n\n // Delete device object and all states (no adapter restart!)\n await this.removeDeviceFromObject(conn.config);\n\n this.updateGlobalConnection();\n }\n\n /**\n * Find connection for a state ID\n *\n * @param stateId Full state ID\n */\n private findConnectionForState(\n stateId: string,\n ): DeviceConnection | undefined {\n const localId = stateId.replace(`${this.namespace}.`, \"\");\n for (const conn of this.connections.values()) {\n const prefix = this.stateManager.devicePrefix(conn.config);\n if (localId.startsWith(`${prefix}.`)) {\n return conn;\n }\n }\n return undefined;\n }\n\n /**\n * Log device error with deduplication\n *\n * @param conn Device connection\n * @param context Error context\n * @param err Error object\n */\n private logDeviceError(\n conn: DeviceConnection,\n context: string,\n err: unknown,\n ): void {\n const code =\n err instanceof HomeWizardApiError\n ? err.errorCode\n : err instanceof Error\n ? err.message\n : \"unknown\";\n const key = `${context}:${code}`;\n\n if (conn.lastErrorCode === key) {\n // Same error as last time \u2014 debug only\n this.log.debug(\n `${conn.config.productName} (${conn.ip}) ${context}: ${code}`,\n );\n } else {\n conn.lastErrorCode = key;\n this.log.warn(\n `${conn.config.productName} (${conn.ip}) ${context}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) =>\n new HomeWizard(options);\n} else {\n (() => new HomeWizard())();\n}\n"],
|
|
5
|
-
"mappings": "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",
|
|
4
|
+
"sourcesContent": ["import * as utils from \"@iobroker/adapter-core\";\nimport { HomeWizardDiscovery } from \"./lib/discovery\";\nimport { HomeWizardApiError, HomeWizardClient } from \"./lib/homewizard-client\";\nimport { StateManager } from \"./lib/state-manager\";\nimport type {\n DeviceConfig,\n DeviceConnection,\n DiscoveredDevice,\n Measurement,\n} from \"./lib/types\";\nimport { HomeWizardWebSocket } from \"./lib/websocket-client\";\n\n/** Pairing timeout in milliseconds (60 seconds) */\nconst PAIRING_TIMEOUT_MS = 60_000;\n/** Pairing poll interval in milliseconds */\nconst PAIRING_POLL_MS = 2_000;\n/** WebSocket reconnect base delay in milliseconds */\nconst WS_RECONNECT_BASE_MS = 5_000;\n/** Maximum WebSocket reconnect delay in milliseconds */\nconst WS_RECONNECT_MAX_MS = 300_000;\n/** REST fallback poll interval in milliseconds */\nconst REST_POLL_MS = 10_000;\n/** System info poll interval in milliseconds */\nconst SYSTEM_POLL_MS = 60_000;\n/** Max auth failures before giving up */\nconst MAX_AUTH_FAILURES = 3;\n\nclass HomeWizard extends utils.Adapter {\n private stateManager!: StateManager;\n private discovery: HomeWizardDiscovery | null = null;\n private readonly connections = new Map<string, DeviceConnection>();\n private pairingTimer: ioBroker.Timeout | undefined = undefined;\n private pairingPollTimer: ioBroker.Interval | undefined = undefined;\n private systemPollTimer: ioBroker.Interval | undefined = undefined;\n private isPairing = false;\n private pairingManualIp = \"\";\n private discoveredDuringPairing: DiscoveredDevice[] = [];\n\n /** @param options Adapter options */\n public constructor(options: Partial<utils.AdapterOptions> = {}) {\n super({ ...options, name: \"homewizard\" });\n this.on(\"ready\", () => this.onReady());\n this.on(\"stateChange\", (id, state) => this.onStateChange(id, state));\n this.on(\"unload\", (callback) => this.onUnload(callback));\n }\n\n /** Adapter started */\n private async onReady(): Promise<void> {\n this.stateManager = new StateManager(this);\n\n // Create pairing states\n await this.extendObjectAsync(\"pairingIp\", {\n type: \"state\",\n common: {\n name: \"Device IP for manual pairing\",\n type: \"string\",\n role: \"text\",\n read: true,\n write: true,\n def: \"\",\n },\n native: {},\n });\n\n // Reset pairing states on start (in case previous run was killed mid-pairing)\n await this.setStateAsync(\"startPairing\", { val: false, ack: true });\n await this.setStateAsync(\"pairingIp\", { val: \"\", ack: true });\n\n // Subscribe to pairing button and writable device states\n await this.subscribeStatesAsync(\"startPairing\");\n await this.subscribeStatesAsync(\"*.system.reboot\");\n await this.subscribeStatesAsync(\"*.system.identify\");\n await this.subscribeStatesAsync(\"*.system.cloud_enabled\");\n await this.subscribeStatesAsync(\"*.system.status_led_brightness_pct\");\n await this.subscribeStatesAsync(\"*.system.api_v1_enabled\");\n await this.subscribeStatesAsync(\"*.battery.mode\");\n await this.subscribeStatesAsync(\"*.battery.permissions\");\n await this.subscribeStatesAsync(\"*.remove\");\n\n // Load devices from device objects (not from adapter config)\n const devices = await this.loadDevicesFromObjects();\n if (devices.length === 0) {\n this.log.info(\n \"No devices configured \u2014 press 'Start Pairing' to add a HomeWizard device\",\n );\n await this.setStateAsync(\"info.connection\", { val: false, ack: true });\n }\n\n // Create connection entries for all configured devices\n for (const device of devices) {\n const key = this.stateManager.devicePrefix(device);\n await this.stateManager.createDeviceStates(device);\n const conn: DeviceConnection = {\n config: device,\n ip: device.ip || \"\",\n wsClient: null,\n wsAuthenticated: false,\n pollTimer: undefined,\n reconnectTimer: undefined,\n wsFailCount: 0,\n authFailCount: 0,\n lastErrorCode: \"\",\n };\n this.connections.set(key, conn);\n\n // If we have a stored IP, connect immediately\n if (conn.ip) {\n this.log.debug(`Using stored IP ${conn.ip} for ${device.productName}`);\n void this.initDevice(conn);\n }\n }\n\n // Start mDNS discovery (updates IP if device announces)\n this.discovery = new HomeWizardDiscovery(this.log);\n this.discovery.start((discovered) => {\n this.onDeviceDiscovered(discovered);\n });\n\n // Periodic system info poll\n this.systemPollTimer = this.setInterval(() => {\n void this.pollAllSystemInfo();\n }, SYSTEM_POLL_MS);\n\n this.updateGlobalConnection();\n }\n\n /**\n * Load device configs from existing device objects\n * Tokens are stored encrypted in device object native\n */\n private async loadDevicesFromObjects(): Promise<DeviceConfig[]> {\n const devices: DeviceConfig[] = [];\n\n // Also migrate from old adapter config if devices exist there\n const oldDevices: DeviceConfig[] =\n ((this.config as Record<string, unknown>).devices as DeviceConfig[]) ||\n [];\n if (oldDevices.length > 0) {\n this.log.debug(\n `Migrating ${oldDevices.length} device(s) from adapter config to device objects`,\n );\n for (const device of oldDevices) {\n await this.saveDeviceToObject(device);\n }\n // Clear old config (this triggers one restart, but only during migration)\n await this.extendForeignObjectAsync(`system.adapter.${this.namespace}`, {\n native: { devices: [] },\n });\n return oldDevices;\n }\n\n // Read device objects from our namespace\n const objects = await this.getAdapterObjectsAsync();\n for (const [id, obj] of Object.entries(objects)) {\n if (obj.type !== \"device\") {\n continue;\n }\n const native = obj.native as Record<string, string> | undefined;\n if (!native?.encryptedToken || !native.serial) {\n continue;\n }\n const localId = id.replace(`${this.namespace}.`, \"\");\n this.log.debug(`Loading device from object: ${localId}`);\n const token = this.decrypt(native.encryptedToken);\n devices.push({\n token,\n productType: native.productType || \"unknown\",\n serial: native.serial,\n productName: native.productName || native.productType || \"unknown\",\n ...(native.ip ? { ip: native.ip } : {}),\n });\n }\n\n return devices;\n }\n\n /**\n * Save device config to its device object native (encrypted token)\n *\n * @param config Device configuration to save\n */\n private async saveDeviceToObject(config: DeviceConfig): Promise<void> {\n const prefix = this.stateManager.devicePrefix(config);\n const encryptedToken = this.encrypt(config.token);\n await this.extendObjectAsync(prefix, {\n type: \"device\",\n common: { name: config.productName || config.productType },\n native: {\n encryptedToken,\n productType: config.productType,\n serial: config.serial,\n productName: config.productName,\n ...(config.ip ? { ip: config.ip } : {}),\n },\n });\n }\n\n /**\n * Remove device config from its device object\n *\n * @param config Device configuration to remove\n */\n private async removeDeviceFromObject(config: DeviceConfig): Promise<void> {\n await this.stateManager.removeDevice(config);\n }\n\n /**\n * Handle a discovered device from mDNS\n *\n * @param discovered Discovered device info\n */\n private onDeviceDiscovered(discovered: DiscoveredDevice): void {\n // During pairing, collect new devices\n if (this.isPairing) {\n const existing = Array.from(this.connections.values()).find(\n (c) => c.config.serial === discovered.serial,\n );\n if (!existing) {\n if (\n !this.discoveredDuringPairing.find(\n (d) => d.serial === discovered.serial,\n )\n ) {\n this.discoveredDuringPairing.push(discovered);\n this.log.info(\n `Found ${discovered.name} (${discovered.productType}) at ${discovered.ip} \u2014 press the button on the device to pair`,\n );\n }\n return;\n }\n }\n\n // Match against configured devices\n for (const [, conn] of this.connections) {\n if (conn.config.serial !== discovered.serial) {\n continue;\n }\n\n // Update IP if changed\n if (conn.ip !== discovered.ip) {\n const oldIp = conn.ip;\n conn.ip = discovered.ip;\n\n if (oldIp) {\n this.log.debug(\n `${conn.config.productName}: IP changed ${oldIp} \u2192 ${discovered.ip}`,\n );\n // Close active WS so it reconnects with new IP\n if (conn.wsClient) {\n conn.wsClient.close();\n conn.wsClient = null;\n conn.wsAuthenticated = false;\n // Cancel pending reconnect \u2014 we reconnect immediately\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n conn.reconnectTimer = undefined;\n }\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n conn.pollTimer = undefined;\n }\n conn.wsFailCount = 0;\n conn.authFailCount = 0;\n this.connectWebSocket(conn);\n }\n }\n }\n\n // Connect if not already connected\n if (!conn.wsClient && !conn.reconnectTimer) {\n this.log.debug(\n `mDNS: found ${conn.config.productName} at ${discovered.ip}`,\n );\n void this.initDevice(conn);\n }\n\n return;\n }\n }\n\n /**\n * Adapter stopping \u2014 MUST be synchronous\n *\n * @param callback Completion callback\n */\n private onUnload(callback: () => void): void {\n if (this.pairingTimer) {\n this.clearTimeout(this.pairingTimer);\n }\n if (this.pairingPollTimer) {\n this.clearInterval(this.pairingPollTimer);\n }\n if (this.systemPollTimer) {\n this.clearInterval(this.systemPollTimer);\n }\n\n this.discovery?.stop();\n\n for (const conn of this.connections.values()) {\n conn.wsClient?.close();\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n }\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n }\n }\n this.connections.clear();\n\n void this.setState(\"info.connection\", { val: false, ack: true });\n callback();\n }\n\n /**\n * Handle state changes\n *\n * @param id State ID\n * @param state State value\n */\n private async onStateChange(\n id: string,\n state: ioBroker.State | null | undefined,\n ): Promise<void> {\n if (!state || state.ack) {\n return;\n }\n\n // Pairing button\n if (id.endsWith(\".startPairing\")) {\n if (state.val) {\n await this.startPairing();\n }\n return;\n }\n\n // Remove device button\n if (id.endsWith(\".remove\")) {\n if (state.val) {\n await this.removeDevice(id);\n }\n return;\n }\n\n // Find which device this state belongs to\n const conn = this.findConnectionForState(id);\n if (!conn || !conn.ip) {\n return;\n }\n\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n\n try {\n if (id.endsWith(\".system.reboot\")) {\n this.log.info(`Rebooting ${conn.config.productName} (${conn.ip})`);\n await client.reboot();\n } else if (id.endsWith(\".system.identify\")) {\n await client.identify();\n } else if (id.endsWith(\".system.cloud_enabled\")) {\n await client.setSystem({ cloud_enabled: !!state.val });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".system.status_led_brightness_pct\")) {\n await client.setSystem({\n status_led_brightness_pct: Number(state.val),\n });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".system.api_v1_enabled\")) {\n await client.setSystem({ api_v1_enabled: !!state.val });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".battery.mode\")) {\n await client.setBatteries({\n mode: String(state.val) as \"zero\" | \"to_full\" | \"standby\",\n });\n await this.setStateAsync(id, { val: state.val, ack: true });\n } else if (id.endsWith(\".battery.permissions\")) {\n const perms = JSON.parse(String(state.val));\n await client.setBatteries({ permissions: perms });\n await this.setStateAsync(id, { val: state.val, ack: true });\n }\n } catch (err) {\n this.log.warn(\n `Failed to set ${id}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n\n /** Start pairing mode \u2014 discover devices and attempt to pair */\n private async startPairing(): Promise<void> {\n if (this.isPairing) {\n this.log.debug(\"Pairing already active\");\n return;\n }\n\n // Reset startPairing immediately so it doesn't survive a restart\n await this.setStateAsync(\"startPairing\", { val: false, ack: true });\n\n this.isPairing = true;\n this.discoveredDuringPairing = [];\n\n // Check if manual IP is set, then clear pairingIp immediately\n const ipState = await this.getStateAsync(\"pairingIp\");\n this.pairingManualIp = ipState?.val ? String(ipState.val).trim() : \"\";\n await this.setStateAsync(\"pairingIp\", { val: \"\", ack: true });\n\n if (this.pairingManualIp) {\n this.log.info(\n `Pairing mode enabled for ${this.pairingManualIp} \u2014 press the button on your HomeWizard device now (60 seconds timeout)`,\n );\n // Add as discovered device immediately\n this.discoveredDuringPairing.push({\n ip: this.pairingManualIp,\n productType: \"unknown\",\n serial: \"unknown\",\n name: this.pairingManualIp,\n });\n } else {\n this.log.info(\n \"Pairing mode enabled \u2014 searching for devices via mDNS, press the button on your HomeWizard device now (60 seconds timeout)\",\n );\n // Restart mDNS browser to trigger fresh query \u2014 already-cached devices\n // won't be re-announced otherwise and pairing would never find them\n if (!this.discovery) {\n this.discovery = new HomeWizardDiscovery(this.log);\n }\n this.discovery.start((discovered) => {\n this.onDeviceDiscovered(discovered);\n });\n }\n\n // Poll discovered devices for pairing\n this.pairingPollTimer = this.setInterval(() => {\n void this.pollPairing();\n }, PAIRING_POLL_MS);\n\n // Timeout pairing\n this.pairingTimer = this.setTimeout(() => {\n this.stopPairing();\n this.log.info(\n \"Pairing mode automatically disabled after 60 seconds timeout\",\n );\n }, PAIRING_TIMEOUT_MS);\n }\n\n /** Poll all discovered devices to attempt pairing */\n private async pollPairing(): Promise<void> {\n for (const device of this.discoveredDuringPairing) {\n try {\n const client = new HomeWizardClient(device.ip);\n const result = await client.requestPairing();\n\n // Success! Button was pressed\n this.log.info(\n `Successfully paired with ${device.name} (${device.productType}) at ${device.ip} \u2014 connecting...`,\n );\n\n // Get device info\n const authedClient = new HomeWizardClient(device.ip, result.token);\n const info = await authedClient.getDeviceInfo();\n\n const deviceConfig: DeviceConfig = {\n token: result.token,\n productType: info.product_type,\n serial: info.serial,\n productName: info.product_name,\n ...(this.pairingManualIp ? { ip: this.pairingManualIp } : {}),\n };\n\n // Save to device object (no adapter restart!)\n await this.saveDeviceToObject(deviceConfig);\n await this.stateManager.createDeviceStates(deviceConfig);\n\n // Create connection and connect\n const key = this.stateManager.devicePrefix(deviceConfig);\n const conn: DeviceConnection = {\n config: deviceConfig,\n ip: device.ip,\n wsClient: null,\n wsAuthenticated: false,\n pollTimer: undefined,\n reconnectTimer: undefined,\n wsFailCount: 0,\n authFailCount: 0,\n lastErrorCode: \"\",\n };\n this.connections.set(key, conn);\n void this.initDevice(conn);\n\n // Remove from discovery list\n this.discoveredDuringPairing = this.discoveredDuringPairing.filter(\n (d) => d.serial !== info.serial,\n );\n\n this.stopPairing();\n this.updateGlobalConnection();\n return;\n } catch (err) {\n // 403 = button not pressed yet \u2014 expected, keep polling\n if (err instanceof HomeWizardApiError && err.statusCode === 403) {\n continue;\n }\n this.log.debug(\n `Pairing poll error for ${device.ip}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n }\n\n /** Stop pairing mode */\n private stopPairing(): void {\n this.isPairing = false;\n this.pairingManualIp = \"\";\n this.discoveredDuringPairing = [];\n\n if (this.pairingPollTimer) {\n this.clearInterval(this.pairingPollTimer);\n this.pairingPollTimer = undefined;\n }\n if (this.pairingTimer) {\n this.clearTimeout(this.pairingTimer);\n this.pairingTimer = undefined;\n }\n }\n\n /**\n * Initialize a newly discovered device \u2014 fetch info and connect WebSocket\n *\n * @param conn Device connection with IP set\n */\n private async initDevice(conn: DeviceConnection): Promise<void> {\n try {\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n const info = await client.getDeviceInfo();\n const key = this.stateManager.devicePrefix(conn.config);\n await this.setStateAsync(`${key}.info.firmware`, {\n val: info.firmware_version,\n ack: true,\n });\n } catch (err) {\n this.logDeviceError(conn, \"init\", err);\n }\n\n this.connectWebSocket(conn);\n void this.pollSystemInfo(conn);\n }\n\n /**\n * Connect WebSocket for a device\n *\n * @param conn Device connection\n */\n private connectWebSocket(conn: DeviceConnection): void {\n if (!conn.ip) {\n return; // No IP yet \u2014 wait for mDNS\n }\n\n // Stop reconnecting if auth keeps failing\n if (conn.authFailCount >= MAX_AUTH_FAILURES) {\n return;\n }\n\n const key = this.stateManager.devicePrefix(conn.config);\n\n const wsClient = new HomeWizardWebSocket(conn.ip, conn.config.token, {\n onMeasurement: (data: Measurement) => {\n void this.stateManager.updateMeasurement(conn.config, data);\n },\n onConnected: () => {\n conn.wsAuthenticated = true;\n conn.wsFailCount = 0;\n conn.authFailCount = 0;\n void this.stateManager.setDeviceConnected(conn.config, true);\n this.updateGlobalConnection();\n\n // Stop REST fallback if active\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n conn.pollTimer = undefined;\n }\n\n // Log restoration if we had errors before\n if (conn.lastErrorCode) {\n this.log.info(`${conn.config.productName}: connection restored`);\n conn.lastErrorCode = \"\";\n }\n\n this.log.debug(\n `WebSocket connected to ${conn.config.productName} (${conn.ip})`,\n );\n },\n onDisconnected: (error?: Error) => {\n conn.wsAuthenticated = false;\n conn.wsClient = null;\n void this.stateManager.setDeviceConnected(conn.config, false);\n this.updateGlobalConnection();\n\n if (error) {\n this.logDeviceError(conn, \"ws\", error);\n }\n\n // Check if this was an auth failure\n if (\n error instanceof HomeWizardApiError &&\n error.errorCode === \"user:unauthorized\"\n ) {\n conn.authFailCount++;\n if (conn.authFailCount >= MAX_AUTH_FAILURES) {\n this.log.warn(\n `${conn.config.productName}: token invalid \u2014 re-pair device to fix`,\n );\n return;\n }\n }\n\n // Start REST fallback\n this.startRestFallback(conn);\n\n // Schedule reconnect with exponential backoff\n conn.wsFailCount++;\n const delay = Math.min(\n WS_RECONNECT_BASE_MS * Math.pow(2, conn.wsFailCount - 1),\n WS_RECONNECT_MAX_MS,\n );\n this.log.debug(\n `${key}: WS reconnect in ${delay / 1000}s (attempt ${conn.wsFailCount})`,\n );\n\n conn.reconnectTimer = this.setTimeout(() => {\n conn.reconnectTimer = undefined;\n this.connectWebSocket(conn);\n }, delay);\n },\n log: this.log,\n });\n\n conn.wsClient = wsClient;\n wsClient.connect();\n }\n\n /**\n * Start REST polling as fallback when WebSocket is down\n *\n * @param conn Device connection\n */\n private startRestFallback(conn: DeviceConnection): void {\n if (conn.pollTimer || !conn.ip) {\n return;\n }\n\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n\n conn.pollTimer = this.setInterval(async () => {\n try {\n const data = await client.getMeasurement();\n await this.stateManager.updateMeasurement(conn.config, data);\n await this.stateManager.setDeviceConnected(conn.config, true);\n } catch (err) {\n this.logDeviceError(conn, \"rest\", err);\n await this.stateManager.setDeviceConnected(conn.config, false);\n }\n this.updateGlobalConnection();\n }, REST_POLL_MS);\n }\n\n /** Poll system info for all connected devices */\n private async pollAllSystemInfo(): Promise<void> {\n for (const conn of this.connections.values()) {\n // Only poll devices that have an IP and are connected or at least reachable\n if (conn.ip && (conn.wsAuthenticated || conn.pollTimer)) {\n await this.pollSystemInfo(conn);\n }\n }\n }\n\n /**\n * Poll system info for a single device\n *\n * @param conn Device connection\n */\n private async pollSystemInfo(conn: DeviceConnection): Promise<void> {\n if (!conn.ip) {\n return;\n }\n\n try {\n const client = new HomeWizardClient(conn.ip, conn.config.token);\n const system = await client.getSystem();\n await this.stateManager.updateSystem(conn.config, system);\n\n // Also poll battery if device supports it\n try {\n const battery = await client.getBatteries();\n // Only create battery states if batteries are actually connected\n if (battery.battery_count && battery.battery_count > 0) {\n await this.stateManager.updateBattery(conn.config, battery);\n }\n } catch {\n // Device may not support batteries \u2014 that's fine\n }\n } catch (err) {\n this.logDeviceError(conn, \"system\", err);\n }\n }\n\n /** Update global info.connection based on all device states */\n private updateGlobalConnection(): void {\n const anyConnected = Array.from(this.connections.values()).some(\n (c) => c.wsAuthenticated,\n );\n void this.setStateAsync(\"info.connection\", {\n val: anyConnected,\n ack: true,\n });\n }\n\n /**\n * Remove a device \u2014 disconnect, delete states and object\n *\n * @param stateId The remove state ID\n */\n private async removeDevice(stateId: string): Promise<void> {\n const conn = this.findConnectionForState(stateId);\n if (!conn) {\n return;\n }\n\n const key = this.stateManager.devicePrefix(conn.config);\n this.log.info(\n `Removing device ${conn.config.productName} (${conn.config.serial})`,\n );\n\n // Disconnect\n conn.wsClient?.close();\n if (conn.pollTimer) {\n this.clearInterval(conn.pollTimer);\n }\n if (conn.reconnectTimer) {\n this.clearTimeout(conn.reconnectTimer);\n }\n this.connections.delete(key);\n\n // Delete device object and all states (no adapter restart!)\n await this.removeDeviceFromObject(conn.config);\n\n this.updateGlobalConnection();\n }\n\n /**\n * Find connection for a state ID\n *\n * @param stateId Full state ID\n */\n private findConnectionForState(\n stateId: string,\n ): DeviceConnection | undefined {\n const localId = stateId.replace(`${this.namespace}.`, \"\");\n for (const conn of this.connections.values()) {\n const prefix = this.stateManager.devicePrefix(conn.config);\n if (localId.startsWith(`${prefix}.`)) {\n return conn;\n }\n }\n return undefined;\n }\n\n /**\n * Log device error with deduplication\n *\n * @param conn Device connection\n * @param context Error context\n * @param err Error object\n */\n private logDeviceError(\n conn: DeviceConnection,\n context: string,\n err: unknown,\n ): void {\n const code =\n err instanceof HomeWizardApiError\n ? err.errorCode\n : err instanceof Error\n ? err.message\n : \"unknown\";\n const key = `${context}:${code}`;\n\n if (conn.lastErrorCode === key) {\n // Same error as last time \u2014 debug only\n this.log.debug(\n `${conn.config.productName} (${conn.ip}) ${context}: ${code}`,\n );\n } else {\n conn.lastErrorCode = key;\n this.log.warn(\n `${conn.config.productName} (${conn.ip}) ${context}: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n }\n}\n\nif (require.main !== module) {\n module.exports = (options: Partial<utils.AdapterOptions> | undefined) =>\n new HomeWizard(options);\n} else {\n (() => new HomeWizard())();\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": "homewizard",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.3",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.3.3": {
|
|
7
|
+
"en": "Fix mDNS pairing not finding devices, improve log messages during pairing",
|
|
8
|
+
"de": "mDNS-Pairing findet Geraete wieder, verbesserte Log-Meldungen beim Pairing",
|
|
9
|
+
"ru": "Исправлено обнаружение устройств через mDNS при сопряжении, улучшены сообщения в логе",
|
|
10
|
+
"pt": "Corrigida descoberta de dispositivos via mDNS durante emparelhamento, mensagens de log melhoradas",
|
|
11
|
+
"nl": "mDNS-koppeling vindt apparaten weer, verbeterde logberichten tijdens koppelen",
|
|
12
|
+
"fr": "Correction de la decouverte mDNS lors de l appairage, messages de log ameliores",
|
|
13
|
+
"it": "Corretta scoperta mDNS durante l associazione, messaggi di log migliorati",
|
|
14
|
+
"es": "Corregido descubrimiento mDNS durante emparejamiento, mensajes de log mejorados",
|
|
15
|
+
"pl": "Naprawiono wykrywanie mDNS podczas parowania, ulepszone komunikaty w logu",
|
|
16
|
+
"uk": "Виправлено виявлення mDNS пiд час сполучення, покращенi повiдомлення в лозi",
|
|
17
|
+
"zh-cn": "修复 mDNS 配对时未发现设备的问题,改进配对日志消息"
|
|
18
|
+
},
|
|
19
|
+
"0.3.2": {
|
|
20
|
+
"en": "Improve Admin UI and README with detailed configuration guide (prerequisites, manual IP pairing)",
|
|
21
|
+
"de": "Admin UI und README verbessert mit detaillierter Konfigurationsanleitung (Voraussetzungen, manuelles IP-Pairing)",
|
|
22
|
+
"ru": "Улучшен Admin UI и README с подробным руководством по настройке (предварительные требования, ручное IP-сопряжение)",
|
|
23
|
+
"pt": "Admin UI e README melhorados com guia de configuração detalhado (pré-requisitos, emparelhamento manual por IP)",
|
|
24
|
+
"nl": "Admin UI en README verbeterd met gedetailleerde configuratiegids (vereisten, handmatig IP-koppelen)",
|
|
25
|
+
"fr": "Admin UI et README améliorés avec guide de configuration détaillé (prérequis, appairage IP manuel)",
|
|
26
|
+
"it": "Admin UI e README migliorati con guida di configurazione dettagliata (prerequisiti, associazione IP manuale)",
|
|
27
|
+
"es": "Admin UI y README mejorados con guía de configuración detallada (requisitos, emparejamiento IP manual)",
|
|
28
|
+
"pl": "Ulepszono Admin UI i README ze szczegółowym przewodnikiem konfiguracji (wymagania, ręczne parowanie IP)",
|
|
29
|
+
"uk": "Покращено Admin UI та README з детальним посібником з налаштування (передумови, ручне IP-сполучення)",
|
|
30
|
+
"zh-cn": "改进 Admin UI 和 README,提供详细配置指南(前提条件、手动 IP 配对)"
|
|
31
|
+
},
|
|
6
32
|
"0.3.1": {
|
|
7
33
|
"en": "Fix uncaught exception on device removal (invalid WebSocket frame during close)",
|
|
8
34
|
"de": "Uncaught Exception beim Geraet entfernen behoben (ungueltiger WebSocket-Frame beim Schliessen)",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "Dołączono certyfikat CA HomeWizard dla prawidłowej walidacji TLS zamiast pomijania weryfikacji",
|
|
68
94
|
"uk": "Додано CA-сертифікат HomeWizard для належної перевірки TLS замість пропуску верифікації",
|
|
69
95
|
"zh-cn": "捆绑 HomeWizard CA 证书进行正确的 TLS 验证,而非跳过验证"
|
|
70
|
-
},
|
|
71
|
-
"0.1.1": {
|
|
72
|
-
"en": "Add unit tests (129 tests), fix Dependabot config",
|
|
73
|
-
"de": "Unit-Tests hinzugefügt (129 Tests), Dependabot-Konfiguration korrigiert",
|
|
74
|
-
"ru": "Добавлены модульные тесты (129 тестов), исправлена конфигурация Dependabot",
|
|
75
|
-
"pt": "Adicionados testes unitários (129 testes), corrigida configuração do Dependabot",
|
|
76
|
-
"nl": "Unit tests toegevoegd (129 tests), Dependabot-configuratie gecorrigeerd",
|
|
77
|
-
"fr": "Ajout de tests unitaires (129 tests), correction de la configuration Dependabot",
|
|
78
|
-
"it": "Aggiunti test unitari (129 test), corretta configurazione Dependabot",
|
|
79
|
-
"es": "Añadidos tests unitarios (129 tests), corregida configuración de Dependabot",
|
|
80
|
-
"pl": "Dodano testy jednostkowe (129 testów), poprawiono konfigurację Dependabot",
|
|
81
|
-
"uk": "Додано модульні тести (129 тестів), виправлено конфігурацію Dependabot",
|
|
82
|
-
"zh-cn": "添加单元测试(129个测试),修复 Dependabot 配置"
|
|
83
|
-
},
|
|
84
|
-
"0.1.0": {
|
|
85
|
-
"en": "Initial release with API v2, WebSocket push, mDNS discovery, multi-device pairing",
|
|
86
|
-
"de": "Erstveröffentlichung mit API v2, WebSocket-Push, mDNS-Discovery, Multi-Geräte-Pairing",
|
|
87
|
-
"ru": "Первый выпуск с API v2, WebSocket push, mDNS discovery, сопряжение нескольких устройств",
|
|
88
|
-
"pt": "Lançamento inicial com API v2, WebSocket push, descoberta mDNS, emparelhamento multi-dispositivo",
|
|
89
|
-
"nl": "Eerste release met API v2, WebSocket push, mDNS discovery, multi-device pairing",
|
|
90
|
-
"fr": "Version initiale avec API v2, WebSocket push, découverte mDNS, appairage multi-appareils",
|
|
91
|
-
"it": "Rilascio iniziale con API v2, WebSocket push, scoperta mDNS, associazione multi-dispositivo",
|
|
92
|
-
"es": "Lanzamiento inicial con API v2, WebSocket push, descubrimiento mDNS, emparejamiento multi-dispositivo",
|
|
93
|
-
"pl": "Pierwsze wydanie z API v2, WebSocket push, odkrywanie mDNS, parowanie wielu urządzeń",
|
|
94
|
-
"uk": "Початковий випуск з API v2, WebSocket push, mDNS виявлення, з'єднання кількох пристроїв",
|
|
95
|
-
"zh-cn": "初始版本,支持 API v2、WebSocket 推送、mDNS 发现、多设备配对"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
package/package.json
CHANGED