iobroker.laundrylens 0.4.16 → 0.4.18
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 +18 -2
- package/admin/i18n/de.json +5 -0
- package/admin/i18n/en.json +5 -0
- package/admin/i18n/es.json +5 -0
- package/admin/i18n/fr.json +5 -0
- package/admin/i18n/it.json +5 -0
- package/admin/i18n/nl.json +5 -0
- package/admin/i18n/pl.json +5 -0
- package/admin/i18n/pt.json +5 -0
- package/admin/i18n/ru.json +5 -0
- package/admin/i18n/uk.json +5 -0
- package/admin/i18n/zh-cn.json +5 -0
- package/admin/jsonConfig.json +46 -0
- package/admin/phaseLabels.json +14 -0
- package/admin/tab_m.html +18 -14
- package/io-package.json +27 -27
- package/lib/displayLabels.js +98 -0
- package/lib/washDataManager.js +31 -0
- package/main.js +81 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -31,6 +31,7 @@ The adapter is self-learning: there are no pre-built profiles. Every program is
|
|
|
31
31
|
- Telegram notifications with configurable update thresholds, placeholders (`{progress}`, `{prevTime}`, `{state:objectId}` for any ioBroker data point) and conditional text blocks
|
|
32
32
|
- Also supports Pushover, Signal, WhatsApp, Matrix, notify-my-android, Prowl, and email (via [ioBroker.email](https://github.com/iobroker-community-adapters/ioBroker.email)) as notification targets
|
|
33
33
|
- Multiple devices: one instance per device (washing machine, dryer, …)
|
|
34
|
+
- Localized, ready-to-display text data points (`phaseText`, `stateText`, `programText`) alongside the language-neutral internal ones, for dropping straight into a VIS dashboard
|
|
34
35
|
|
|
35
36
|
---
|
|
36
37
|
|
|
@@ -77,19 +78,24 @@ The defaults are tuned for Siemens iQ appliances. Detection threshold is a trade
|
|
|
77
78
|
|
|
78
79
|
| Data point | Type | Description |
|
|
79
80
|
|---|---|---|
|
|
80
|
-
| `state` | string | off / starting / running / paused / ending |
|
|
81
|
+
| `state` | string | off / starting / running / paused / ending (language-neutral internal key, meant for automations) |
|
|
82
|
+
| `stateText` | string | Same as `state`, but as ready-to-display, localized text (e.g. "Running ⚙️" / "Läuft ⚙️") - see [`displayLanguage`](#configuration) |
|
|
81
83
|
| `running` | boolean | Simple on/off indicator |
|
|
82
84
|
| `program` | string | Detected program name |
|
|
85
|
+
| `programText` | string | Same as `program`, but with the `detecting...` placeholder localized (a confirmed program is your own saved name, passed through unchanged) |
|
|
83
86
|
| `confidence` | number | Match confidence in % |
|
|
84
87
|
| `timeRemaining` | number | Estimated remaining time in seconds |
|
|
85
88
|
| `totalDuration` | number | Estimated total cycle duration in seconds |
|
|
86
89
|
| `cycleProgress` | number | Cycle progress 0–100 % |
|
|
87
|
-
| `phase` | string | Current cycle phase |
|
|
90
|
+
| `phase` | string | Current cycle phase (language-neutral internal key, e.g. `washing`, `dryer_drying`) |
|
|
91
|
+
| `phaseText` | string | Same as `phase`, but as ready-to-display, localized text with an emoji (e.g. "🫧 Washing" / "🫧 Wäscht") |
|
|
88
92
|
| `lastCycleProgram` | string | Program of the last completed cycle |
|
|
89
93
|
| `lastCycleDuration` | number | Duration of the last cycle in minutes |
|
|
90
94
|
| `lastCycleEnergy` | number | Energy consumed in the last cycle in Wh |
|
|
91
95
|
| `availablePrograms` | string (JSON) | Array of all saved program names, e.g. for external dropdowns |
|
|
92
96
|
|
|
97
|
+
`phase`/`state`/`program` are meant for automations/scripts and use fixed, language-neutral values that never change with your ioBroker language - use the matching `*Text` data point instead if you want to show status directly in a VIS dashboard without building your own translation table. The language for `phaseText`/`stateText`/`programText` defaults to the ioBroker system language, and can be overridden per device with the "Display language" setting.
|
|
98
|
+
|
|
93
99
|
---
|
|
94
100
|
|
|
95
101
|
## Using data points in notification messages
|
|
@@ -126,6 +132,16 @@ This also works inside the conditional `[...]` blocks: `[🌡️ Outside: {state
|
|
|
126
132
|
|
|
127
133
|
### **WORK IN PROGRESS**
|
|
128
134
|
|
|
135
|
+
### 0.4.18 (2026-09-02)
|
|
136
|
+
- Fix: the dryer anti-crease lock period (learned duration + a fixed 10-minute buffer) could expire while the dryer was still genuinely doing its anti-crease tumbling - real anti-crease duration varies cycle to cycle, so a fixed length occasionally ran out mid-tumble, letting the tail end trigger a false new-cycle detection. The lock now extends when a genuine tumble spike occurs near its expiry, capped at 90 minutes total from the beep
|
|
137
|
+
|
|
138
|
+
### 0.4.17 (2026-08-31)
|
|
139
|
+
- New: `phaseText`, `stateText`, `programText` data points - localized, ready-to-display versions of the existing language-neutral `phase`/`state`/`program`, useful for a VIS dashboard without building your own translation table
|
|
140
|
+
- New: "Display language" setting per device (defaults to the ioBroker system language)
|
|
141
|
+
- Internal: the phase-translation table (previously only in the admin tab, for the cycle graph legend) is now one shared file (`admin/phaseLabels.json`) used by both the admin UI and the new server-side data points, instead of two separately maintained copies
|
|
142
|
+
- Translated remaining German code comments to English (not a review/checker requirement - developer-only comments in German are explicitly permitted - done on request)
|
|
143
|
+
- Reorganized the README changelog: the current 0.4.x series stays here in full, the pre-0.4.0 beta history (0.2.2–0.3.0) moved entirely to CHANGELOG_OLD.md (previously one version, 0.2.5, existed with near-duplicate text in both places)
|
|
144
|
+
|
|
129
145
|
### 0.4.16 (2026-08-31)
|
|
130
146
|
- New: notification templates support a `{state:objectId}` placeholder that resolves any ioBroker data point's current value at send time (e.g. outside temperature, electricity price), in addition to the existing built-in placeholders. Works inside conditional `[...]` blocks too - a block hides itself if the referenced data point is empty
|
|
131
147
|
- Fix: a leftover German word ("Fertig") in the notification subject line
|
package/admin/i18n/de.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Gerätename",
|
|
40
40
|
"Device type": "Gerätetyp",
|
|
41
41
|
"Device:": "Gerät:",
|
|
42
|
+
"Display language": "Anzeigesprache",
|
|
42
43
|
"Display name in the dashboard": "Anzeigename im Dashboard",
|
|
43
44
|
"Dryer settings": "Trockner-Einstellungen",
|
|
44
45
|
"Duration": "Dauer",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Sofortübernahme ab Konfidenz (%)",
|
|
66
67
|
"Invalid JSON file": "Ungültige JSON-Datei",
|
|
67
68
|
"JSON appears here after export…": "JSON erscheint hier nach Export…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Sprache für phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Letzter Zyklus",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Letzter Zyklus nicht sicher erkannt. Bitte bestätigen oder korrigieren.",
|
|
70
72
|
"Learned on": "Gelernt am",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Start-Energie-Gate (Wh)",
|
|
130
132
|
"Starting…": "Startet…",
|
|
131
133
|
"Status": "Status",
|
|
134
|
+
"System default": "Systemstandard",
|
|
132
135
|
"Test message sent!": "Testnachricht gesendet!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Die Datenpunkte phase/state/program (z.B. 'washing', 'off') sind sprachneutrale interne Schlüssel, gedacht für Automatisierungen. Dies steuert die Sprache der zusätzlichen *Text-Datenpunkte (z.B. 'phaseText'), die eine fertige, anzeigbare Übersetzung enthalten - nützlich, um den Status direkt in einem VIS-Dashboard anzuzeigen, ohne selbst zu übersetzen. Standardmäßig die ioBroker-Systemsprache.",
|
|
133
137
|
"Time remaining": "Restzeit",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Damit das Muster auch genutzt wird, \"Gespeichertes Anti-Knitter-Muster ignorieren\" in den Geräte-Einstellungen ausschalten.",
|
|
135
139
|
"Trim": "Trimmen",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Falsches Programm",
|
|
139
143
|
"confidence": "Konfidenz",
|
|
140
144
|
"cycles": "Zyklen",
|
|
145
|
+
"detecting...": "erkenne...",
|
|
141
146
|
"e.g. Cotton 60°": "z.B. Baumwolle 60°",
|
|
142
147
|
"e.g. Washing machine": "z.B. Waschmaschine",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "z.B. name@example.com (leer = Adapter-Standard)",
|
package/admin/i18n/en.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Device name",
|
|
40
40
|
"Device type": "Device type",
|
|
41
41
|
"Device:": "Device:",
|
|
42
|
+
"Display language": "Display language",
|
|
42
43
|
"Display name in the dashboard": "Display name in the dashboard",
|
|
43
44
|
"Dryer settings": "Dryer settings",
|
|
44
45
|
"Duration": "Duration",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Instant adoption from confidence (%)",
|
|
66
67
|
"Invalid JSON file": "Invalid JSON file",
|
|
67
68
|
"JSON appears here after export…": "JSON appears here after export…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Language for phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Last cycle",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Last cycle not confidently detected. Please confirm or correct.",
|
|
70
72
|
"Learned on": "Learned on",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Start energy gate (Wh)",
|
|
130
132
|
"Starting…": "Starting…",
|
|
131
133
|
"Status": "Status",
|
|
134
|
+
"System default": "System default",
|
|
132
135
|
"Test message sent!": "Test message sent!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.",
|
|
133
137
|
"Time remaining": "Time remaining",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.",
|
|
135
139
|
"Trim": "Trim",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Wrong program",
|
|
139
143
|
"confidence": "confidence",
|
|
140
144
|
"cycles": "cycles",
|
|
145
|
+
"detecting...": "detecting...",
|
|
141
146
|
"e.g. Cotton 60°": "e.g. Cotton 60°",
|
|
142
147
|
"e.g. Washing machine": "e.g. Washing machine",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "e.g. name@example.com (blank = adapter default)",
|
package/admin/i18n/es.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Nombre del dispositivo",
|
|
40
40
|
"Device type": "Tipo de dispositivo",
|
|
41
41
|
"Device:": "Dispositivo:",
|
|
42
|
+
"Display language": "Idioma de visualización",
|
|
42
43
|
"Display name in the dashboard": "Nombre mostrado en el panel",
|
|
43
44
|
"Dryer settings": "Configuración de la secadora",
|
|
44
45
|
"Duration": "Duración",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Adopción instantánea a partir de la confianza (%)",
|
|
66
67
|
"Invalid JSON file": "Archivo JSON no válido",
|
|
67
68
|
"JSON appears here after export…": "El JSON aparece aquí después de la exportación…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Idioma para phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Último ciclo",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "El último ciclo no se detectó con confianza. Confirma o corrige.",
|
|
70
72
|
"Learned on": "Aprendido el",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Umbral de energía de inicio (Wh)",
|
|
130
132
|
"Starting…": "Iniciando…",
|
|
131
133
|
"Status": "Estado",
|
|
134
|
+
"System default": "Predeterminado del sistema",
|
|
132
135
|
"Test message sent!": "¡Mensaje de prueba enviado!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Los puntos de datos phase/state/program (p. ej. 'washing', 'off') son claves internas independientes del idioma, pensadas para automatizaciones. Esto controla el idioma de los puntos de datos *Text adicionales (p. ej. 'phaseText') que contienen una traducción lista para mostrar - útil para mostrar el estado directamente en un panel VIS sin traducirlo usted mismo. Por defecto, el idioma del sistema de ioBroker.",
|
|
133
137
|
"Time remaining": "Tiempo restante",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Para que este patrón se use realmente, desactiva \"Ignorar el patrón antiarrugas guardado\" en la configuración del dispositivo.",
|
|
135
139
|
"Trim": "Recortar",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Programa incorrecto",
|
|
139
143
|
"confidence": "confianza",
|
|
140
144
|
"cycles": "ciclos",
|
|
145
|
+
"detecting...": "detectando...",
|
|
141
146
|
"e.g. Cotton 60°": "p. ej. Algodón 60°",
|
|
142
147
|
"e.g. Washing machine": "p. ej. Lavadora",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "p. ej. nombre@ejemplo.com (vacío = valor predeterminado del adaptador)",
|
package/admin/i18n/fr.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Nom de l'appareil",
|
|
40
40
|
"Device type": "Type d'appareil",
|
|
41
41
|
"Device:": "Appareil :",
|
|
42
|
+
"Display language": "Langue d'affichage",
|
|
42
43
|
"Display name in the dashboard": "Nom affiché dans le tableau de bord",
|
|
43
44
|
"Dryer settings": "Paramètres du sèche-linge",
|
|
44
45
|
"Duration": "Durée",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Adoption instantanée à partir de la confiance (%)",
|
|
66
67
|
"Invalid JSON file": "Fichier JSON invalide",
|
|
67
68
|
"JSON appears here after export…": "Le JSON apparaît ici après l'export…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Langue pour phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Dernier cycle",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Dernier cycle non détecté avec certitude. Veuillez confirmer ou corriger.",
|
|
70
72
|
"Learned on": "Appris le",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Seuil d'énergie de démarrage (Wh)",
|
|
130
132
|
"Starting…": "Démarrage…",
|
|
131
133
|
"Status": "Statut",
|
|
134
|
+
"System default": "Par défaut du système",
|
|
132
135
|
"Test message sent!": "Message de test envoyé !",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Les points de données phase/state/program (ex. 'washing', 'off') sont des clés internes indépendantes de la langue, destinées aux automatisations. Ceci contrôle la langue des points de données *Text supplémentaires (ex. 'phaseText') qui contiennent une traduction prête à afficher - utile pour afficher le statut directement dans un tableau de bord VIS sans le traduire vous-même. Par défaut, la langue système d'ioBroker.",
|
|
133
137
|
"Time remaining": "Temps restant",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Pour que ce motif soit réellement utilisé, désactivez « Ignorer le motif anti-froissage enregistré » dans les paramètres de l'appareil.",
|
|
135
139
|
"Trim": "Rogner",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Mauvais programme",
|
|
139
143
|
"confidence": "confiance",
|
|
140
144
|
"cycles": "cycles",
|
|
145
|
+
"detecting...": "détection...",
|
|
141
146
|
"e.g. Cotton 60°": "p. ex. Coton 60°",
|
|
142
147
|
"e.g. Washing machine": "p. ex. Lave-linge",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "p. ex. nom@exemple.com (vide = valeur par défaut de l'adaptateur)",
|
package/admin/i18n/it.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Nome del dispositivo",
|
|
40
40
|
"Device type": "Tipo di dispositivo",
|
|
41
41
|
"Device:": "Dispositivo:",
|
|
42
|
+
"Display language": "Lingua di visualizzazione",
|
|
42
43
|
"Display name in the dashboard": "Nome visualizzato nella dashboard",
|
|
43
44
|
"Dryer settings": "Impostazioni asciugatrice",
|
|
44
45
|
"Duration": "Durata",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Adozione immediata a partire dalla confidenza (%)",
|
|
66
67
|
"Invalid JSON file": "File JSON non valido",
|
|
67
68
|
"JSON appears here after export…": "Il JSON appare qui dopo l'esportazione…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Lingua per phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Ultimo ciclo",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Ultimo ciclo non rilevato con certezza. Conferma o correggi.",
|
|
70
72
|
"Learned on": "Appreso il",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Soglia energia di avvio (Wh)",
|
|
130
132
|
"Starting…": "Avvio…",
|
|
131
133
|
"Status": "Stato",
|
|
134
|
+
"System default": "Predefinito di sistema",
|
|
132
135
|
"Test message sent!": "Messaggio di prova inviato!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "I punti dati phase/state/program (es. 'washing', 'off') sono chiavi interne indipendenti dalla lingua, pensate per le automazioni. Questo controlla la lingua dei punti dati *Text aggiuntivi (es. 'phaseText') che contengono una traduzione pronta per la visualizzazione - utile per mostrare lo stato direttamente in una dashboard VIS senza doverlo tradurre da soli. Per impostazione predefinita usa la lingua di sistema di ioBroker.",
|
|
133
137
|
"Time remaining": "Tempo rimanente",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Per usare davvero questo modello, disattiva \"Ignora il modello anti-piega salvato\" nelle impostazioni del dispositivo.",
|
|
135
139
|
"Trim": "Taglia",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Programma errato",
|
|
139
143
|
"confidence": "confidenza",
|
|
140
144
|
"cycles": "cicli",
|
|
145
|
+
"detecting...": "rilevamento...",
|
|
141
146
|
"e.g. Cotton 60°": "es. Cotone 60°",
|
|
142
147
|
"e.g. Washing machine": "es. Lavatrice",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "es. nome@esempio.com (vuoto = predefinito dell'adattatore)",
|
package/admin/i18n/nl.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Apparaatnaam",
|
|
40
40
|
"Device type": "Apparaattype",
|
|
41
41
|
"Device:": "Apparaat:",
|
|
42
|
+
"Display language": "Weergavetaal",
|
|
42
43
|
"Display name in the dashboard": "Weergavenaam in het dashboard",
|
|
43
44
|
"Dryer settings": "Drogerinstellingen",
|
|
44
45
|
"Duration": "Duur",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Directe overname vanaf betrouwbaarheid (%)",
|
|
66
67
|
"Invalid JSON file": "Ongeldig JSON-bestand",
|
|
67
68
|
"JSON appears here after export…": "JSON verschijnt hier na export…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Taal voor phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Laatste cyclus",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Laatste cyclus niet zeker herkend. Bevestig of corrigeer.",
|
|
70
72
|
"Learned on": "Geleerd op",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Startenergiedrempel (Wh)",
|
|
130
132
|
"Starting…": "Start…",
|
|
131
133
|
"Status": "Status",
|
|
134
|
+
"System default": "Systeemstandaard",
|
|
132
135
|
"Test message sent!": "Testbericht verzonden!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "De datapunten phase/state/program (bijv. 'washing', 'off') zijn taalneutrale interne sleutels, bedoeld voor automatiseringen. Dit bepaalt de taal van de extra *Text-datapunten (bijv. 'phaseText') die een kant-en-klare vertaling bevatten - handig om de status direct in een VIS-dashboard te tonen zonder zelf te vertalen. Standaard de ioBroker-systeemtaal.",
|
|
133
137
|
"Time remaining": "Resterende tijd",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Om dit patroon daadwerkelijk te gebruiken, zet \"Opgeslagen kreukbeschermingspatroon negeren\" uit in de apparaatinstellingen.",
|
|
135
139
|
"Trim": "Bijsnijden",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Verkeerd programma",
|
|
139
143
|
"confidence": "betrouwbaarheid",
|
|
140
144
|
"cycles": "cycli",
|
|
145
|
+
"detecting...": "detecteren...",
|
|
141
146
|
"e.g. Cotton 60°": "bijv. Katoen 60°",
|
|
142
147
|
"e.g. Washing machine": "bijv. Wasmachine",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "bijv. naam@voorbeeld.com (leeg = adapterstandaard)",
|
package/admin/i18n/pl.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Nazwa urządzenia",
|
|
40
40
|
"Device type": "Typ urządzenia",
|
|
41
41
|
"Device:": "Urządzenie:",
|
|
42
|
+
"Display language": "Język wyświetlania",
|
|
42
43
|
"Display name in the dashboard": "Nazwa wyświetlana na pulpicie",
|
|
43
44
|
"Dryer settings": "Ustawienia suszarki",
|
|
44
45
|
"Duration": "Czas trwania",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Natychmiastowe przyjęcie od pewności (%)",
|
|
66
67
|
"Invalid JSON file": "Nieprawidłowy plik JSON",
|
|
67
68
|
"JSON appears here after export…": "JSON pojawi się tutaj po eksporcie…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Język dla phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Ostatni cykl",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Ostatni cykl nie został wykryty z pewnością. Potwierdź lub popraw.",
|
|
70
72
|
"Learned on": "Nauczono się",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Próg energii startowej (Wh)",
|
|
130
132
|
"Starting…": "Uruchamianie…",
|
|
131
133
|
"Status": "Status",
|
|
134
|
+
"System default": "Domyślny systemowy",
|
|
132
135
|
"Test message sent!": "Wysłano wiadomość testową!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Punkty danych phase/state/program (np. 'washing', 'off') to niezależne od języka klucze wewnętrzne, przeznaczone do automatyzacji. To ustawienie kontroluje język dodatkowych punktów danych *Text (np. 'phaseText'), które zawierają gotowe do wyświetlenia tłumaczenie - przydatne do pokazywania statusu bezpośrednio w panelu VIS bez własnoręcznego tłumaczenia. Domyślnie język systemowy ioBroker.",
|
|
133
137
|
"Time remaining": "Pozostały czas",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Aby ten wzorzec był rzeczywiście używany, wyłącz \"Ignoruj zapisany wzorzec przeciw gnieceniu\" w ustawieniach urządzenia.",
|
|
135
139
|
"Trim": "Przytnij",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Zły program",
|
|
139
143
|
"confidence": "pewność",
|
|
140
144
|
"cycles": "cykli",
|
|
145
|
+
"detecting...": "wykrywanie...",
|
|
141
146
|
"e.g. Cotton 60°": "np. Bawełna 60°",
|
|
142
147
|
"e.g. Washing machine": "np. Pralka",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "np. nazwa@przyklad.com (puste = domyślne adaptera)",
|
package/admin/i18n/pt.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Nome do dispositivo",
|
|
40
40
|
"Device type": "Tipo de dispositivo",
|
|
41
41
|
"Device:": "Dispositivo:",
|
|
42
|
+
"Display language": "Idioma de exibição",
|
|
42
43
|
"Display name in the dashboard": "Nome de exibição no painel",
|
|
43
44
|
"Dryer settings": "Configurações da secadora",
|
|
44
45
|
"Duration": "Duração",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Adoção instantânea a partir da confiança (%)",
|
|
66
67
|
"Invalid JSON file": "Arquivo JSON inválido",
|
|
67
68
|
"JSON appears here after export…": "O JSON aparece aqui após a exportação…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Idioma para phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Último ciclo",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "O último ciclo não foi detectado com confiança. Confirme ou corrija.",
|
|
70
72
|
"Learned on": "Aprendido em",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Limiar de energia de início (Wh)",
|
|
130
132
|
"Starting…": "Iniciando…",
|
|
131
133
|
"Status": "Status",
|
|
134
|
+
"System default": "Padrão do sistema",
|
|
132
135
|
"Test message sent!": "Mensagem de teste enviada!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Os pontos de dados phase/state/program (por ex. 'washing', 'off') são chaves internas independentes de idioma, destinadas a automações. Isso controla o idioma dos pontos de dados *Text adicionais (por ex. 'phaseText') que contêm uma tradução pronta para exibição - útil para mostrar o status diretamente em um painel VIS sem traduzir você mesmo. O padrão é o idioma do sistema ioBroker.",
|
|
133
137
|
"Time remaining": "Tempo restante",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Para que este padrão seja realmente usado, desative \"Ignorar padrão salvo de anti-amarrotamento\" nas configurações do dispositivo.",
|
|
135
139
|
"Trim": "Aparar",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Programa errado",
|
|
139
143
|
"confidence": "confiança",
|
|
140
144
|
"cycles": "ciclos",
|
|
145
|
+
"detecting...": "detectando...",
|
|
141
146
|
"e.g. Cotton 60°": "ex. Algodão 60°",
|
|
142
147
|
"e.g. Washing machine": "ex. Máquina de lavar",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "ex. nome@exemplo.com (vazio = padrão do adaptador)",
|
package/admin/i18n/ru.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Имя устройства",
|
|
40
40
|
"Device type": "Тип устройства",
|
|
41
41
|
"Device:": "Устройство:",
|
|
42
|
+
"Display language": "Язык отображения",
|
|
42
43
|
"Display name in the dashboard": "Отображаемое имя на панели",
|
|
43
44
|
"Dryer settings": "Настройки сушилки",
|
|
44
45
|
"Duration": "Длительность",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Мгновенное принятие от уверенности (%)",
|
|
66
67
|
"Invalid JSON file": "Неверный JSON-файл",
|
|
67
68
|
"JSON appears here after export…": "JSON появится здесь после экспорта…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Язык для phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Последний цикл",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Последний цикл определён неуверенно. Пожалуйста, подтвердите или исправьте.",
|
|
70
72
|
"Learned on": "Изучено",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Порог стартовой энергии (Втч)",
|
|
130
132
|
"Starting…": "Запускается…",
|
|
131
133
|
"Status": "Статус",
|
|
134
|
+
"System default": "Системный по умолчанию",
|
|
132
135
|
"Test message sent!": "Тестовое сообщение отправлено!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Точки данных phase/state/program (например, 'washing', 'off') - это независимые от языка внутренние ключи, предназначенные для автоматизаций. Это управляет языком дополнительных точек данных *Text (например, 'phaseText'), которые содержат готовый к отображению перевод - полезно для показа статуса прямо в панели VIS без самостоятельного перевода. По умолчанию используется системный язык ioBroker.",
|
|
133
137
|
"Time remaining": "Оставшееся время",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Чтобы шаблон действительно использовался, отключите «Игнорировать сохранённый шаблон защиты от сминания» в настройках устройства.",
|
|
135
139
|
"Trim": "Обрезать",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Неправильная программа",
|
|
139
143
|
"confidence": "уверенность",
|
|
140
144
|
"cycles": "циклов",
|
|
145
|
+
"detecting...": "определение...",
|
|
141
146
|
"e.g. Cotton 60°": "напр. Хлопок 60°",
|
|
142
147
|
"e.g. Washing machine": "напр. Стиральная машина",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "напр. name@example.com (пусто = по умолчанию адаптера)",
|
package/admin/i18n/uk.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "Назва пристрою",
|
|
40
40
|
"Device type": "Тип пристрою",
|
|
41
41
|
"Device:": "Пристрій:",
|
|
42
|
+
"Display language": "Мова відображення",
|
|
42
43
|
"Display name in the dashboard": "Ім'я, що відображається на панелі",
|
|
43
44
|
"Dryer settings": "Налаштування сушарки",
|
|
44
45
|
"Duration": "Тривалість",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "Миттєве прийняття від довіри (%)",
|
|
66
67
|
"Invalid JSON file": "Недійсний файл JSON",
|
|
67
68
|
"JSON appears here after export…": "JSON з'явиться тут після експорту…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "Мова для phaseText/stateText/programText",
|
|
68
70
|
"Last cycle": "Останній цикл",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "Останній цикл визначено невпевнено. Підтвердіть або виправте.",
|
|
70
72
|
"Learned on": "Вивчено",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "Поріг стартової енергії (Втгод)",
|
|
130
132
|
"Starting…": "Запускається…",
|
|
131
133
|
"Status": "Статус",
|
|
134
|
+
"System default": "Системна за замовчуванням",
|
|
132
135
|
"Test message sent!": "Тестове повідомлення надіслано!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "Точки даних phase/state/program (наприклад, 'washing', 'off') - це незалежні від мови внутрішні ключі, призначені для автоматизацій. Це керує мовою додаткових точок даних *Text (наприклад, 'phaseText'), які містять готовий до показу переклад - корисно для показу статусу прямо на панелі VIS без самостійного перекладу. За замовчуванням використовується системна мова ioBroker.",
|
|
133
137
|
"Time remaining": "Час, що залишився",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "Щоб цей шаблон дійсно використовувався, вимкніть «Ігнорувати збережений шаблон захисту від зминання» в налаштуваннях пристрою.",
|
|
135
139
|
"Trim": "Обрізати",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "Неправильна програма",
|
|
139
143
|
"confidence": "довіра",
|
|
140
144
|
"cycles": "циклів",
|
|
145
|
+
"detecting...": "визначення...",
|
|
141
146
|
"e.g. Cotton 60°": "напр. Бавовна 60°",
|
|
142
147
|
"e.g. Washing machine": "напр. Пральна машина",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "напр. name@example.com (порожньо = типове значення адаптера)",
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -39,6 +39,7 @@
|
|
|
39
39
|
"Device name": "设备名称",
|
|
40
40
|
"Device type": "设备类型",
|
|
41
41
|
"Device:": "设备:",
|
|
42
|
+
"Display language": "显示语言",
|
|
42
43
|
"Display name in the dashboard": "仪表板中显示的名称",
|
|
43
44
|
"Dryer settings": "烘干机设置",
|
|
44
45
|
"Duration": "时长",
|
|
@@ -65,6 +66,7 @@
|
|
|
65
66
|
"Instant adoption from confidence (%)": "即时采用置信度(%)",
|
|
66
67
|
"Invalid JSON file": "无效的JSON文件",
|
|
67
68
|
"JSON appears here after export…": "导出后JSON将显示在此处…",
|
|
69
|
+
"Language for phaseText/stateText/programText": "phaseText/stateText/programText 的语言",
|
|
68
70
|
"Last cycle": "上次周期",
|
|
69
71
|
"Last cycle not confidently detected. Please confirm or correct.": "未能确信地检测到上次周期。请确认或更正。",
|
|
70
72
|
"Learned on": "学习于",
|
|
@@ -129,7 +131,9 @@
|
|
|
129
131
|
"Start energy gate (Wh)": "启动能量阈值(瓦时)",
|
|
130
132
|
"Starting…": "启动中…",
|
|
131
133
|
"Status": "状态",
|
|
134
|
+
"System default": "系统默认",
|
|
132
135
|
"Test message sent!": "测试消息已发送!",
|
|
136
|
+
"The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.": "phase/state/program 数据点(例如 'washing'、'off')是与语言无关的内部键,供自动化使用。此设置控制额外的 *Text 数据点(例如 'phaseText')的语言,这些数据点包含可直接显示的翻译——便于在 VIS 仪表盘中直接显示状态而无需自行翻译。默认使用 ioBroker 系统语言。",
|
|
133
137
|
"Time remaining": "剩余时间",
|
|
134
138
|
"To actually use this pattern, turn off \"Ignore saved anti-crease pattern\" in the device settings.": "要真正使用此模式,请在设备设置中关闭\"忽略已保存的防皱模式\"。",
|
|
135
139
|
"Trim": "修剪",
|
|
@@ -138,6 +142,7 @@
|
|
|
138
142
|
"Wrong program": "错误的程序",
|
|
139
143
|
"confidence": "置信度",
|
|
140
144
|
"cycles": "个周期",
|
|
145
|
+
"detecting...": "检测中...",
|
|
141
146
|
"e.g. Cotton 60°": "例如:棉质60°",
|
|
142
147
|
"e.g. Washing machine": "例如:洗衣机",
|
|
143
148
|
"e.g. name@example.com (blank = adapter default)": "例如:name@example.com(留空=适配器默认值)",
|
package/admin/jsonConfig.json
CHANGED
|
@@ -261,6 +261,52 @@
|
|
|
261
261
|
"md": 6,
|
|
262
262
|
"lg": 4,
|
|
263
263
|
"xl": 4
|
|
264
|
+
},
|
|
265
|
+
"_sep4": {
|
|
266
|
+
"type": "divider",
|
|
267
|
+
"xs": 12,
|
|
268
|
+
"sm": 12,
|
|
269
|
+
"md": 12,
|
|
270
|
+
"lg": 12,
|
|
271
|
+
"xl": 12
|
|
272
|
+
},
|
|
273
|
+
"_hdr4": {
|
|
274
|
+
"type": "staticText",
|
|
275
|
+
"text": "Display language",
|
|
276
|
+
"style": {
|
|
277
|
+
"fontWeight": "bold",
|
|
278
|
+
"marginTop": "8px"
|
|
279
|
+
},
|
|
280
|
+
"xs": 12,
|
|
281
|
+
"sm": 12,
|
|
282
|
+
"md": 12,
|
|
283
|
+
"lg": 12,
|
|
284
|
+
"xl": 12
|
|
285
|
+
},
|
|
286
|
+
"displayLanguage": {
|
|
287
|
+
"type": "select",
|
|
288
|
+
"label": "Language for phaseText/stateText/programText",
|
|
289
|
+
"options": [
|
|
290
|
+
{ "label": "System default", "value": "" },
|
|
291
|
+
{ "label": "English", "value": "en" },
|
|
292
|
+
{ "label": "Deutsch", "value": "de" },
|
|
293
|
+
{ "label": "Русский", "value": "ru" },
|
|
294
|
+
{ "label": "Português", "value": "pt" },
|
|
295
|
+
{ "label": "Nederlands", "value": "nl" },
|
|
296
|
+
{ "label": "Français", "value": "fr" },
|
|
297
|
+
{ "label": "Italiano", "value": "it" },
|
|
298
|
+
{ "label": "Español", "value": "es" },
|
|
299
|
+
{ "label": "Polski", "value": "pl" },
|
|
300
|
+
{ "label": "Українська", "value": "uk" },
|
|
301
|
+
{ "label": "中文 (简体)", "value": "zh-cn" }
|
|
302
|
+
],
|
|
303
|
+
"default": "",
|
|
304
|
+
"help": "The phase/state/program data points (e.g. 'washing', 'off') are language-neutral internal keys, meant for automations. This controls the language of the extra *Text data points (e.g. 'phaseText') that hold a ready-to-display translation - useful for showing status directly in a VIS dashboard without translating it yourself. Defaults to the ioBroker system language.",
|
|
305
|
+
"xs": 12,
|
|
306
|
+
"sm": 12,
|
|
307
|
+
"md": 6,
|
|
308
|
+
"lg": 4,
|
|
309
|
+
"xl": 4
|
|
264
310
|
}
|
|
265
311
|
}
|
|
266
312
|
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"heating": { "emoji": "🔥", "en": "Heating", "de": "Aufheizen", "ru": "Нагрев", "pt": "Aquecendo", "nl": "Verwarmen", "fr": "Chauffe", "it": "Riscaldamento", "es": "Calentando", "pl": "Podgrzewanie", "uk": "Нагрівання", "zh-cn": "加热" },
|
|
3
|
+
"washing": { "emoji": "🫧", "en": "Washing", "de": "Wäscht", "ru": "Стирка", "pt": "Lavando", "nl": "Wast", "fr": "Lavage", "it": "Lavaggio", "es": "Lavando", "pl": "Pranie", "uk": "Прання", "zh-cn": "洗涤" },
|
|
4
|
+
"rinsing": { "emoji": "💧", "en": "Rinsing", "de": "Spült", "ru": "Полоскание", "pt": "Enxaguando", "nl": "Spoelt", "fr": "Rinçage", "it": "Risciacquo", "es": "Aclarando", "pl": "Płukanie", "uk": "Полоскання", "zh-cn": "漂洗" },
|
|
5
|
+
"spinning": { "emoji": "🌀", "en": "Spinning", "de": "Schleudert", "ru": "Отжим", "pt": "Centrifugando", "nl": "Centrifugeert", "fr": "Essorage", "it": "Centrifuga", "es": "Centrifugando", "pl": "Wirowanie", "uk": "Віджим", "zh-cn": "脱水" },
|
|
6
|
+
"soaking": { "emoji": "🪣", "en": "Soaking", "de": "Einweichen", "ru": "Замачивание", "pt": "De molho", "nl": "Weken", "fr": "Trempage", "it": "Ammollo", "es": "En remojo", "pl": "Namaczanie", "uk": "Замочування", "zh-cn": "浸泡" },
|
|
7
|
+
"dryer_drying": { "emoji": "♨️", "en": "Drying", "de": "Trocknet", "ru": "Сушка", "pt": "Secando", "nl": "Droogt", "fr": "Séchage", "it": "Asciugatura", "es": "Secando", "pl": "Suszenie", "uk": "Сушіння", "zh-cn": "烘干" },
|
|
8
|
+
"cooling": { "emoji": "❄️", "en": "Cooling down", "de": "Abkühlen", "ru": "Охлаждение", "pt": "Arrefecendo", "nl": "Afkoelen", "fr": "Refroidissement", "it": "Raffreddamento", "es": "Enfriando", "pl": "Chłodzenie", "uk": "Охолодження", "zh-cn": "冷却" },
|
|
9
|
+
"anticrease": { "emoji": "👕", "en": "Anti-crease", "de": "Anti-Knitter", "ru": "Защита от сминания", "pt": "Antiamarrotamento", "nl": "Kreukbescherming", "fr": "Anti-froissage", "it": "Antipiega", "es": "Antiarrugas", "pl": "Zapob. gnieceniu", "uk": "Захист від зминання", "zh-cn": "防皱" },
|
|
10
|
+
"prewash": { "emoji": "🚿", "en": "Pre-wash", "de": "Vorspülen", "ru": "Предварительная мойка", "pt": "Pré-lavagem", "nl": "Voorspoelen", "fr": "Prélavage", "it": "Prelavaggio", "es": "Prelavado", "pl": "Wstępne mycie", "uk": "Попереднє миття", "zh-cn": "预洗" },
|
|
11
|
+
"mainwash": { "emoji": "🔥", "en": "Main wash", "de": "Hauptspülgang", "ru": "Основная мойка", "pt": "Lavagem principal", "nl": "Hoofdwas", "fr": "Lavage principal", "it": "Lavaggio principale", "es": "Lavado principal", "pl": "Mycie główne", "uk": "Основне миття", "zh-cn": "主洗" },
|
|
12
|
+
"finalrinse": { "emoji": "✨", "en": "Final rinse", "de": "Klarspülgang", "ru": "Финальное полоскание", "pt": "Enxágue final", "nl": "Naspoelen", "fr": "Rinçage final", "it": "Risciacquo finale", "es": "Aclarado final", "pl": "Płukanie końcowe", "uk": "Фінальне полоскання", "zh-cn": "终洗" },
|
|
13
|
+
"dish_drying": { "emoji": "💨", "en": "Drying", "de": "Trocknet", "ru": "Сушка", "pt": "Secando", "nl": "Droogt", "fr": "Séchage", "it": "Asciugatura", "es": "Secando", "pl": "Suszenie", "uk": "Сушіння", "zh-cn": "烘干" }
|
|
14
|
+
}
|
package/admin/tab_m.html
CHANGED
|
@@ -464,20 +464,7 @@ var PHASE_BG_COLORS = {
|
|
|
464
464
|
|
|
465
465
|
// Display label (emoji + translated text) per phase key and language.
|
|
466
466
|
// Falls back to English if the current admin language isn't listed here.
|
|
467
|
-
var PHASE_LABELS = {
|
|
468
|
-
heating: { emoji: '🔥', en: 'Heating', de: 'Aufheizen', ru: 'Нагрев', pt: 'Aquecendo', nl: 'Verwarmen', fr: 'Chauffe', it: 'Riscaldamento', es: 'Calentando', pl: 'Podgrzewanie', uk: 'Нагрівання', 'zh-cn': '加热' },
|
|
469
|
-
washing: { emoji: '🫧', en: 'Washing', de: 'Wäscht', ru: 'Стирка', pt: 'Lavando', nl: 'Wast', fr: 'Lavage', it: 'Lavaggio', es: 'Lavando', pl: 'Pranie', uk: 'Прання', 'zh-cn': '洗涤' },
|
|
470
|
-
rinsing: { emoji: '💧', en: 'Rinsing', de: 'Spült', ru: 'Полоскание', pt: 'Enxaguando', nl: 'Spoelt', fr: 'Rinçage', it: 'Risciacquo', es: 'Aclarando', pl: 'Płukanie', uk: 'Полоскання', 'zh-cn': '漂洗' },
|
|
471
|
-
spinning: { emoji: '🌀', en: 'Spinning', de: 'Schleudert', ru: 'Отжим', pt: 'Centrifugando', nl: 'Centrifugeert', fr: 'Essorage', it: 'Centrifuga', es: 'Centrifugando', pl: 'Wirowanie', uk: 'Віджим', 'zh-cn': '脱水' },
|
|
472
|
-
soaking: { emoji: '🪣', en: 'Soaking', de: 'Einweichen', ru: 'Замачивание', pt: 'De molho', nl: 'Weken', fr: 'Trempage', it: 'Ammollo', es: 'En remojo', pl: 'Namaczanie', uk: 'Замочування', 'zh-cn': '浸泡' },
|
|
473
|
-
dryer_drying: { emoji: '♨️', en: 'Drying', de: 'Trocknet', ru: 'Сушка', pt: 'Secando', nl: 'Droogt', fr: 'Séchage', it: 'Asciugatura', es: 'Secando', pl: 'Suszenie', uk: 'Сушіння', 'zh-cn': '烘干' },
|
|
474
|
-
cooling: { emoji: '❄️', en: 'Cooling down', de: 'Abkühlen', ru: 'Охлаждение', pt: 'Arrefecendo', nl: 'Afkoelen', fr: 'Refroidissement', it: 'Raffreddamento', es: 'Enfriando', pl: 'Chłodzenie', uk: 'Охолодження', 'zh-cn': '冷却' },
|
|
475
|
-
anticrease: { emoji: '👕', en: 'Anti-crease', de: 'Anti-Knitter', ru: 'Защита от сминания', pt: 'Antiamarrotamento', nl: 'Kreukbescherming', fr: 'Anti-froissage', it: 'Antipiega', es: 'Antiarrugas', pl: 'Zapob. gnieceniu', uk: 'Захист від зминання', 'zh-cn': '防皱' },
|
|
476
|
-
prewash: { emoji: '🚿', en: 'Pre-wash', de: 'Vorspülen', ru: 'Предварительная мойка', pt: 'Pré-lavagem', nl: 'Voorspoelen', fr: 'Prélavage', it: 'Prelavaggio', es: 'Prelavado', pl: 'Wstępne mycie', uk: 'Попереднє миття', 'zh-cn': '预洗' },
|
|
477
|
-
mainwash: { emoji: '🔥', en: 'Main wash', de: 'Hauptspülgang', ru: 'Основная мойка', pt: 'Lavagem principal', nl: 'Hoofdwas', fr: 'Lavage principal', it: 'Lavaggio principale', es: 'Lavado principal', pl: 'Mycie główne', uk: 'Основне миття', 'zh-cn': '主洗' },
|
|
478
|
-
finalrinse: { emoji: '✨', en: 'Final rinse', de: 'Klarspülgang', ru: 'Финальное полоскание', pt: 'Enxágue final', nl: 'Naspoelen', fr: 'Rinçage final', it: 'Risciacquo finale', es: 'Aclarado final', pl: 'Płukanie końcowe', uk: 'Фінальне полоскання', 'zh-cn': '终洗' },
|
|
479
|
-
dish_drying: { emoji: '💨', en: 'Drying', de: 'Trocknet', ru: 'Сушка', pt: 'Secando', nl: 'Droogt', fr: 'Séchage', it: 'Asciugatura', es: 'Secando', pl: 'Suszenie', uk: 'Сушіння', 'zh-cn': '烘干' },
|
|
480
|
-
};
|
|
467
|
+
var PHASE_LABELS = {}; // loaded from admin/phaseLabels.json (shared with the server side)
|
|
481
468
|
|
|
482
469
|
var _currentLang = null; // cached, detected once per admin session
|
|
483
470
|
var _translations = {}; // key → translated string, for the current language
|
|
@@ -517,6 +504,22 @@ function loadTranslations(callback) {
|
|
|
517
504
|
});
|
|
518
505
|
}
|
|
519
506
|
|
|
507
|
+
/**
|
|
508
|
+
* Loads the shared phase-label table (admin/phaseLabels.json) into
|
|
509
|
+
* PHASE_LABELS. Same file the server side reads (lib/displayLabels.js) -
|
|
510
|
+
* one source of truth for phase translations instead of two copies that
|
|
511
|
+
* could drift apart.
|
|
512
|
+
*
|
|
513
|
+
* @param {Function} callback – called once loaded (also on failure, with
|
|
514
|
+
* PHASE_LABELS left empty - getPhaseLabel() falls back to the raw key)
|
|
515
|
+
*/
|
|
516
|
+
function loadPhaseLabels(callback) {
|
|
517
|
+
fetch('phaseLabels.json')
|
|
518
|
+
.then(function(r) { if (!r.ok) throw new Error('not found'); return r.json(); })
|
|
519
|
+
.then(function(json) { PHASE_LABELS = json; callback(); })
|
|
520
|
+
.catch(function() { callback(); });
|
|
521
|
+
}
|
|
522
|
+
|
|
520
523
|
/**
|
|
521
524
|
* Translates a UI string by its English source text (used as the lookup
|
|
522
525
|
* key, per ioBroker i18n convention). Falls back to the key itself if no
|
|
@@ -735,6 +738,7 @@ function setActiveDevice(deviceId) {
|
|
|
735
738
|
// ── Init ──────────────────────────────────────────
|
|
736
739
|
function init() {
|
|
737
740
|
loadTranslations(function() { applyTranslations(); });
|
|
741
|
+
loadPhaseLabels(function() {});
|
|
738
742
|
var allDevices = [];
|
|
739
743
|
var checked = 0;
|
|
740
744
|
var MAX = 5;
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "laundrylens",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.18",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.4.18": {
|
|
7
|
+
"en": "Fix: the dryer anti-crease lock period (which prevents anti-crease tumbling after the beep from being mistaken for a new cycle) had a fixed length - learned duration plus a 10-minute buffer. Real anti-crease duration varies cycle to cycle, so the lock could occasionally expire while the dryer was still genuinely tumbling, letting the tail end trigger a false new-cycle detection. The lock is now adaptive: a genuine anti-crease tumble spike extends it further, capped at 90 minutes total from the beep.",
|
|
8
|
+
"de": "Fix: Die Anti-Knitter-Sperrzeit beim Trockner (verhindert, dass Nachlauf-Tummeln nach dem Piepton als neuer Zyklus fehlinterpretiert wird) hatte eine feste Länge - gelernte Dauer plus 10 Minuten Puffer. Die reale Anti-Knitter-Dauer variiert von Zyklus zu Zyklus, wodurch die Sperre gelegentlich ablaufen konnte, während der Trockner noch tatsächlich tummelte, sodass das Ende fälschlich als neuer Zyklus erkannt wurde. Die Sperre ist jetzt adaptiv: Ein echter Tummel-Spike verlängert sie weiter, insgesamt begrenzt auf 90 Minuten ab dem Piepton.",
|
|
9
|
+
"ru": "Исправление: период блокировки защиты от сминания у сушильной машины (предотвращает ошибочное распознавание остаточного вращения после сигнала как нового цикла) имел фиксированную длину - изученная длительность плюс 10-минутный буфер. Реальная длительность варьируется от цикла к циклу, поэтому блокировка иногда истекала, пока сушилка ещё действительно вращалась. Теперь блокировка адаптивная: настоящий всплеск вращения продлевает её, максимум 90 минут от сигнала.",
|
|
10
|
+
"pt": "Correção: o período de bloqueio anti-amarrotamento da secadora (evita que o movimento residual após o sinal seja confundido com um novo ciclo) tinha duração fixa - duração aprendida mais um buffer de 10 minutos. A duração real varia de ciclo para ciclo, então o bloqueio podia expirar enquanto a secadora ainda estava girando de verdade. Agora o bloqueio é adaptativo: um pico genuíno de movimento o estende, limitado a 90 minutos no total desde o sinal.",
|
|
11
|
+
"nl": "Fix: de anti-kreukvergrendelingsperiode van de droger (voorkomt dat na-tuimelen na de piep als nieuwe cyclus wordt gezien) had een vaste lengte - geleerde duur plus 10 minuten buffer. De echte duur varieert per cyclus, waardoor de vergrendeling soms afliep terwijl de droger nog echt aan het tuimelen was. De vergrendeling is nu adaptief: een echte tuimelpiek verlengt hem verder, met een maximum van 90 minuten vanaf de piep.",
|
|
12
|
+
"fr": "Correction : la période de verrouillage anti-froissage du sèche-linge (empêche que le mouvement résiduel après le bip soit confondu avec un nouveau cycle) avait une durée fixe - durée apprise plus un tampon de 10 minutes. La durée réelle varie d'un cycle à l'autre, le verrouillage pouvait donc parfois expirer alors que le sèche-linge tournait encore réellement. Le verrouillage est désormais adaptatif : un vrai pic de mouvement le prolonge, plafonné à 90 minutes au total depuis le bip.",
|
|
13
|
+
"it": "Correzione: il periodo di blocco anti-piega dell'asciugatrice (evita che il movimento residuo dopo il segnale venga scambiato per un nuovo ciclo) aveva una durata fissa - durata appresa più un buffer di 10 minuti. La durata reale varia da ciclo a ciclo, quindi il blocco poteva scadere mentre l'asciugatrice stava ancora davvero girando. Ora il blocco è adattivo: un vero picco di movimento lo estende, con un limite massimo di 90 minuti totali dal segnale.",
|
|
14
|
+
"es": "Corrección: el período de bloqueo antiarrugas de la secadora (evita que el movimiento residual tras el pitido se confunda con un nuevo ciclo) tenía una duración fija - duración aprendida más un margen de 10 minutos. La duración real varía de un ciclo a otro, por lo que el bloqueo podía expirar mientras la secadora aún seguía girando realmente. Ahora el bloqueo es adaptativo: un pico genuino de movimiento lo extiende, con un límite máximo de 90 minutos desde el pitido.",
|
|
15
|
+
"pl": "Poprawka: okres blokady ochrony przed gnieceniem w suszarce (zapobiega myleniu doczesywania po sygnale z nowym cyklem) miał stały czas trwania - wyuczony czas plus 10-minutowy bufor. Rzeczywisty czas różni się między cyklami, więc blokada mogła czasem wygasnąć, gdy suszarka nadal faktycznie się kręciła. Blokada jest teraz adaptacyjna: prawdziwy skok ruchu przedłuża ją, maksymalnie do 90 minut łącznie od sygnału.",
|
|
16
|
+
"uk": "Виправлення: період блокування захисту від зминання сушарки (запобігає помилковому розпізнаванню залишкового обертання після сигналу як нового циклу) мав фіксовану тривалість - вивчена тривалість плюс 10-хвилинний буфер. Реальна тривалість відрізняється від циклу до циклу, тому блокування могло вичерпатися, поки сушарка ще справді оберталася. Тепер блокування адаптивне: справжній сплеск обертання продовжує його, максимум 90 хвилин від сигналу.",
|
|
17
|
+
"zh-cn": "修复:烘干机防皱锁定期(防止提示音后的残余滚动被误判为新周期)此前是固定时长——学习到的时长加 10 分钟缓冲。实际防皱时长因周期而异,因此锁定有时会在烘干机仍在真实滚动时提前到期,导致尾声被误判为新周期。现在锁定改为自适应:真实的滚动波动会延长锁定,总时长从提示音起最多延长至 90 分钟。"
|
|
18
|
+
},
|
|
19
|
+
"0.4.17": {
|
|
20
|
+
"en": "New: three localized, ready-to-display data points - phaseText, stateText, programText - alongside the existing language-neutral ones (phase/state/program). Useful for showing status directly in a VIS dashboard without building your own translation table. Language defaults to the ioBroker system language, overridable per device via the new \"Display language\" setting. The phase-translation table (already used for the admin tab's cycle graph legend) is now a single shared file used by both the admin UI and these new data points, instead of two separately maintained copies.",
|
|
21
|
+
"de": "Neu: drei lokalisierte, fertig anzeigbare Datenpunkte - phaseText, stateText, programText - zusätzlich zu den bisherigen sprachneutralen (phase/state/program). Nützlich, um den Status direkt in einem VIS-Dashboard anzuzeigen, ohne selbst eine Übersetzungstabelle zu bauen. Die Sprache ist standardmäßig die ioBroker-Systemsprache, pro Gerät überschreibbar über die neue Einstellung „Anzeigesprache\". Die Phasen-Übersetzungstabelle (bisher schon für die Legende des Zyklus-Graphen im Admin-Tab genutzt) ist jetzt eine einzige gemeinsame Datei für Admin-Oberfläche und diese neuen Datenpunkte, statt zweier getrennt gepflegter Kopien.",
|
|
22
|
+
"ru": "Новое: три локализованные, готовые к отображению точки данных - phaseText, stateText, programText - в дополнение к существующим независимым от языка (phase/state/program). Полезно для отображения статуса прямо на панели VIS без создания собственной таблицы переводов. Язык по умолчанию - системный язык ioBroker, можно переопределить для каждого устройства через новую настройку «Язык отображения».",
|
|
23
|
+
"pt": "Novo: três pontos de dados localizados e prontos para exibição - phaseText, stateText, programText - além dos já existentes independentes de idioma (phase/state/program). Útil para mostrar o status diretamente em um painel VIS sem criar sua própria tabela de tradução. O idioma padrão é o idioma do sistema ioBroker, substituível por dispositivo através da nova configuração \"Idioma de exibição\".",
|
|
24
|
+
"nl": "Nieuw: drie gelokaliseerde, direct weergeefbare datapunten - phaseText, stateText, programText - naast de bestaande taalneutrale (phase/state/program). Handig om de status direct in een VIS-dashboard te tonen zonder zelf een vertaaltabel te bouwen. Taal standaard de ioBroker-systeemtaal, per apparaat overschrijfbaar via de nieuwe instelling \"Weergavetaal\".",
|
|
25
|
+
"fr": "Nouveau : trois points de données localisés et prêts à afficher - phaseText, stateText, programText - en plus des points de données neutres existants (phase/state/program). Utile pour afficher le statut directement dans un tableau de bord VIS sans créer sa propre table de traduction. Langue par défaut la langue système d'ioBroker, modifiable par appareil via le nouveau réglage « Langue d'affichage ».",
|
|
26
|
+
"it": "Novità: tre punti dati localizzati e pronti per la visualizzazione - phaseText, stateText, programText - oltre a quelli esistenti indipendenti dalla lingua (phase/state/program). Utile per mostrare lo stato direttamente in una dashboard VIS senza creare una propria tabella di traduzione. Lingua predefinita quella di sistema di ioBroker, sovrascrivibile per dispositivo tramite la nuova impostazione \"Lingua di visualizzazione\".",
|
|
27
|
+
"es": "Novedad: tres puntos de datos localizados y listos para mostrar - phaseText, stateText, programText - además de los existentes independientes del idioma (phase/state/program). Útil para mostrar el estado directamente en un panel VIS sin crear su propia tabla de traducción. Idioma predeterminado el del sistema ioBroker, sobrescribible por dispositivo mediante el nuevo ajuste \"Idioma de visualización\".",
|
|
28
|
+
"pl": "Nowość: trzy zlokalizowane, gotowe do wyświetlenia punkty danych - phaseText, stateText, programText - oprócz istniejących niezależnych od języka (phase/state/program). Przydatne do pokazywania statusu bezpośrednio w panelu VIS bez tworzenia własnej tabeli tłumaczeń. Domyślnie język systemowy ioBroker, można nadpisać dla każdego urządzenia nowym ustawieniem \"Język wyświetlania\".",
|
|
29
|
+
"uk": "Новинка: три локалізовані, готові до показу точки даних - phaseText, stateText, programText - на додаток до наявних незалежних від мови (phase/state/program). Корисно для показу статусу прямо на панелі VIS без створення власної таблиці перекладів. Мова за замовчуванням - системна мова ioBroker, можна перевизначити для кожного пристрою новим налаштуванням «Мова відображення».",
|
|
30
|
+
"zh-cn": "新功能:新增三个本地化、可直接显示的数据点——phaseText、stateText、programText——与现有的语言无关数据点(phase/state/program)并存。便于在 VIS 仪表盘中直接显示状态,无需自建翻译表。语言默认使用 ioBroker 系统语言,可通过新增的\"显示语言\"设置按设备覆盖。"
|
|
31
|
+
},
|
|
6
32
|
"0.4.16": {
|
|
7
33
|
"en": "New: notification message templates now support a `{state:objectId}` placeholder that resolves any ioBroker data point's current value at send time (e.g. outside temperature, electricity price) - on top of the existing built-in placeholders. Works inside conditional `[...]` blocks too: a block hides itself if the referenced data point is empty. Also fixed a leftover German word (\"Fertig\") in the notification subject line.",
|
|
8
34
|
"de": "Neu: Benachrichtigungs-Vorlagen unterstützen jetzt einen `{state:objectId}`-Platzhalter, der den aktuellen Wert eines beliebigen ioBroker-Datenpunkts beim Versenden einsetzt (z.B. Außentemperatur, Strompreis) - zusätzlich zu den bisherigen fest eingebauten Platzhaltern. Funktioniert auch innerhalb der `[...]`-Bedingungsblöcke: Ein Block verschwindet, wenn der referenzierte Datenpunkt leer ist. Außerdem ein übrig gebliebenes deutsches Wort (\"Fertig\") in der Benachrichtigungs-Betreffzeile behoben.",
|
|
@@ -67,32 +93,6 @@
|
|
|
67
93
|
"pl": "Poprawki z ręcznej recenzji ioBroker.repositories (PR #6459): wszystkie komunikaty dziennika i nazwy stanów są teraz w języku angielskim; stan przycisku forceFinish ma teraz poprawnie read: false; istniejące instalacje są automatycznie migrowane przy następnym uruchomieniu; poprawiono sekcję wymagań README. Naprawiono też błąd, który praktycznie uniemożliwiał obsługę wielu urządzeń na jednej instancji adaptera. Dedykowany interfejs administratora do tego jest wciąż opracowywany osobno.",
|
|
68
94
|
"uk": "Виправлення за результатами ручної перевірки ioBroker.repositories (PR #6459): усі повідомлення журналу та назви станів тепер англійською; стан-кнопка forceFinish тепер коректно має read: false; наявні інсталяції автоматично мігрують під час наступного запуску; виправлено розділ вимог README. Також виправлено помилку, яка робила практично неможливим запуск кількох пристроїв на одній інстанції адаптера. Окремий інтерфейс адміністратора для цього ще розробляється.",
|
|
69
95
|
"zh-cn": "来自 ioBroker.repositories 人工审查(PR #6459)的修复:所有日志消息和状态名称现在均为英文;forceFinish 按钮状态现在正确设置为 read: false;现有安装将在下次启动时自动迁移;已更正 README 的要求部分。同时修复了一个使单个适配器实例上运行多个设备实际上不可能实现的错误。用于此目的的专用管理界面仍在单独开发中。"
|
|
70
|
-
},
|
|
71
|
-
"0.4.11": {
|
|
72
|
-
"en": "Fix: audited every remaining admin UI setting for the same class of bug fixed in 0.4.10 (a field silently never reaching the code that uses it) and found one more: \"Notify also for probable program\" (notifyOnProbable) was read in code but never returned by the internal device-config lookup, so checking it had no effect - Telegram updates always waited for the full auto-confirm threshold regardless of this setting. The regression test from 0.4.10 was extended to also check this class of field (read from the device-config lookup directly, not just fields passed into WashDataManager), so this category of bug should now be caught automatically going forward.",
|
|
73
|
-
"de": "Fix: alle übrigen Admin-Einstellungen auf dieselbe Fehlerart geprüft, die in 0.4.10 behoben wurde (ein Feld, das nie beim nutzenden Code ankommt) - und noch einen Fall gefunden: „Auch bei wahrscheinlichem Programm benachrichtigen\" (notifyOnProbable) wurde im Code gelesen, aber vom internen Geräte-Konfigurations-Lookup nie zurückgegeben, sodass die Checkbox wirkungslos war - Telegram-Updates warteten immer auf die volle Auto-Bestätigungs-Schwelle, unabhängig von dieser Einstellung. Der Regressionstest aus 0.4.10 wurde erweitert, um auch diese Art von Feld zu prüfen (direkt aus dem Geräte-Konfigurations-Lookup gelesen, nicht nur an WashDataManager übergebene Felder) - dieser Fehlertyp sollte künftig automatisch auffallen.",
|
|
74
|
-
"ru": "Исправление: проверены все оставшиеся настройки админ-интерфейса на тот же тип ошибки, что был исправлен в 0.4.10 (поле, которое незаметно никогда не доходит до использующего его кода) - и найден ещё один случай: «Уведомлять также при вероятной программе» (notifyOnProbable) считывался в коде, но никогда не возвращался внутренним поиском конфигурации устройства, поэтому включение этого флажка не имело эффекта - уведомления Telegram всегда ждали полного порога автоподтверждения независимо от этой настройки. Регрессионный тест из 0.4.10 расширен для проверки и этого класса полей.",
|
|
75
|
-
"pt": "Correção: auditadas todas as demais configurações da interface admin em busca do mesmo tipo de bug corrigido na 0.4.10 (um campo que silenciosamente nunca chega ao código que o usa) - e encontrado mais um caso: \"Notificar também para programa provável\" (notifyOnProbable) era lido no código, mas nunca retornado pela busca interna de configuração do dispositivo, então marcá-lo não tinha efeito - as atualizações do Telegram sempre esperavam o limite total de confirmação automática, independentemente dessa configuração. O teste de regressão da 0.4.10 foi ampliado para também verificar essa classe de campo.",
|
|
76
|
-
"nl": "Fix: alle overige admin-instellingen gecontroleerd op hetzelfde soort bug dat in 0.4.10 is opgelost (een veld dat stilletjes nooit bij de gebruikende code terechtkomt) - en nog één geval gevonden: \"Ook melden bij waarschijnlijk programma\" (notifyOnProbable) werd in de code gelezen, maar nooit geretourneerd door de interne apparaat-configuratie-opzoeking, waardoor het aanvinken geen effect had - Telegram-updates wachtten altijd op de volledige auto-bevestigingsdrempel, ongeacht deze instelling. De regressietest uit 0.4.10 is uitgebreid om ook dit soort veld te controleren.",
|
|
77
|
-
"fr": "Correction : audit de tous les autres réglages de l'interface admin pour le même type de bug corrigé dans la 0.4.10 (un champ qui n'atteint silencieusement jamais le code qui l'utilise) - et un cas de plus trouvé : « Notifier aussi pour un programme probable » (notifyOnProbable) était lu dans le code mais jamais renvoyé par la recherche interne de configuration de l'appareil, donc cocher la case n'avait aucun effet - les mises à jour Telegram attendaient toujours le seuil complet d'auto-confirmation, quel que soit ce réglage. Le test de régression de la 0.4.10 a été étendu pour vérifier aussi cette catégorie de champ.",
|
|
78
|
-
"it": "Correzione: verificate tutte le altre impostazioni dell'interfaccia admin per lo stesso tipo di bug corretto nella 0.4.10 (un campo che silenziosamente non raggiunge mai il codice che lo usa) - e trovato un altro caso: \"Notifica anche per programma probabile\" (notifyOnProbable) veniva letto nel codice ma mai restituito dalla ricerca interna della configurazione del dispositivo, quindi selezionarlo non aveva alcun effetto - gli aggiornamenti Telegram attendevano sempre la soglia completa di auto-conferma, indipendentemente da questa impostazione. Il test di regressione della 0.4.10 è stato esteso per verificare anche questa categoria di campo.",
|
|
79
|
-
"es": "Corrección: auditadas todas las demás configuraciones de la interfaz de administración en busca del mismo tipo de error corregido en la 0.4.10 (un campo que silenciosamente nunca llega al código que lo usa) - y se encontró un caso más: \"Notificar también para programa probable\" (notifyOnProbable) se leía en el código pero nunca era devuelto por la búsqueda interna de configuración del dispositivo, por lo que marcarlo no tenía efecto - las actualizaciones de Telegram siempre esperaban el umbral completo de auto-confirmación, sin importar esta configuración. La prueba de regresión de la 0.4.10 se amplió para verificar también esta clase de campo.",
|
|
80
|
-
"pl": "Poprawka: sprawdzono wszystkie pozostałe ustawienia interfejsu admina pod kątem tego samego rodzaju błędu naprawionego w 0.4.10 (pole, które nigdy po cichu nie dociera do kodu, który go używa) - i znaleziono jeszcze jeden przypadek: \"Powiadamiaj też przy prawdopodobnym programie\" (notifyOnProbable) było odczytywane w kodzie, ale nigdy nie było zwracane przez wewnętrzne wyszukiwanie konfiguracji urządzenia, więc zaznaczenie go nie miało efektu - powiadomienia Telegram zawsze czekały na pełny próg auto-potwierdzenia, niezależnie od tego ustawienia. Test regresyjny z 0.4.10 został rozszerzony, by sprawdzać także tę klasę pól.",
|
|
81
|
-
"uk": "Виправлення: перевірено всі інші налаштування адмін-інтерфейсу на той самий тип бага, виправленого в 0.4.10 (поле, яке непомітно ніколи не доходить до коду, що його використовує) - і знайдено ще один випадок: «Сповіщати також при ймовірній програмі» (notifyOnProbable) зчитувалося в коді, але ніколи не поверталося внутрішнім пошуком конфігурації пристрою, тому позначення прапорця не мало ефекту - сповіщення Telegram завжди чекали повного порогу автопідтвердження, незалежно від цього налаштування. Регресійний тест з 0.4.10 розширено для перевірки й цього класу полів.",
|
|
82
|
-
"zh-cn": "修复:审查了所有其余的管理界面设置,寻找与 0.4.10 中修复的同类错误(某字段悄悄地从未到达使用它的代码)——又发现了一个:\"可能程序也通知\"(notifyOnProbable)在代码中被读取,但内部设备配置查找从未返回它,因此勾选它没有任何效果——无论此设置如何,Telegram 更新始终等待完整的自动确认阈值。0.4.10 中的回归测试已扩展,以便也检查这一类字段。"
|
|
83
|
-
},
|
|
84
|
-
"0.4.10": {
|
|
85
|
-
"en": "Fix: found the real root cause of the anti-crease false-restart bug (0.4.9 only clarified wording): main.js built the config object passed into WashDataManager field-by-field, and \"ignoreAntiKnitter\" was missing from that list entirely - so the lock-out protection could never activate no matter what the checkbox was set to, even with a reference pattern saved. Also found and fixed two more settings silently affected by the exact same kind of omission, which likewise never took effect regardless of what was configured: \"Program detection threshold (%)\" (matchThreshold) and \"Instant adoption from confidence (%)\" (instantConfirmThreshold). Added a regression test that checks every setting WashDataManager reads is actually passed through from main.js.",
|
|
86
|
-
"de": "Fix: die eigentliche Ursache des Anti-Knitter-Fehl-Neustart-Bugs gefunden (0.4.9 hatte nur die Formulierung klargestellt): main.js baute das an WashDataManager übergebene Config-Objekt Feld für Feld zusammen, und „ignoreAntiKnitter\" fehlte komplett in dieser Liste – die Sperrzeit-Schutzlogik konnte dadurch nie aktiviert werden, egal wie die Checkbox stand, selbst mit gespeichertem Referenzmuster. Zusätzlich zwei weitere Einstellungen gefunden und behoben, die genau demselben Fehler zum Opfer gefallen waren und dadurch nie wirkten, egal was eingestellt war: „Programm-Erkennungs-Schwelle (%)\" (matchThreshold) und „Sofortübernahme ab Konfidenz (%)\" (instantConfirmThreshold). Regressionstest ergänzt, der prüft, dass jede von WashDataManager gelesene Einstellung auch tatsächlich aus main.js durchgereicht wird.",
|
|
87
|
-
"ru": "Исправление: найдена настоящая причина бага с ложным перезапуском из-за защиты от сминания (в 0.4.9 была уточнена только формулировка): main.js собирал объект конфигурации для WashDataManager поле за полем, и «ignoreAntiKnitter» в этом списке полностью отсутствовал — защита с блокировкой поэтому никогда не могла активироваться, независимо от положения чекбокса, даже с сохранённым шаблоном. Также найдены и исправлены ещё две настройки, пострадавшие от такой же ошибки и никогда не действовавшие, что бы ни было настроено: «Порог распознавания программы (%)» (matchThreshold) и «Мгновенное принятие от уверенности (%)» (instantConfirmThreshold). Добавлен регрессионный тест, проверяющий, что каждая настройка, читаемая WashDataManager, действительно передаётся из main.js.",
|
|
88
|
-
"pt": "Correção: encontrada a causa raiz real do bug de reinício falso por anti-amarrotamento (a 0.4.9 apenas esclareceu o texto): o main.js construía o objeto de configuração passado ao WashDataManager campo a campo, e \"ignoreAntiKnitter\" estava totalmente ausente dessa lista - por isso a proteção de bloqueio nunca podia ser ativada, independentemente do estado da caixa de seleção, mesmo com um padrão salvo. Também encontradas e corrigidas mais duas configurações afetadas pelo mesmo tipo de omissão, que igualmente nunca tinham efeito: \"Limite de detecção de programa (%)\" (matchThreshold) e \"Adoção instantânea a partir da confiança (%)\" (instantConfirmThreshold). Adicionado um teste de regressão que verifica se toda configuração lida pelo WashDataManager é realmente repassada pelo main.js.",
|
|
89
|
-
"nl": "Fix: de echte oorzaak van de valse-herstart-bug bij kreukbescherming gevonden (0.4.9 verduidelijkte alleen de tekst): main.js bouwde het configuratie-object voor WashDataManager veld voor veld op, en \"ignoreAntiKnitter\" ontbrak volledig in die lijst - de vergrendelingsbescherming kon daardoor nooit worden geactiveerd, ongeacht de stand van het selectievakje, zelfs met een opgeslagen patroon. Ook nog twee andere instellingen gevonden en verholpen die aan dezelfde soort omissie leden en daardoor nooit effect hadden: \"Programmaherkenningsdrempel (%)\" (matchThreshold) en \"Direct overnemen vanaf betrouwbaarheid (%)\" (instantConfirmThreshold). Regressietest toegevoegd die controleert of elke instelling die WashDataManager leest ook echt vanuit main.js wordt doorgegeven.",
|
|
90
|
-
"fr": "Correction : la véritable cause du bug de faux redémarrage anti-froissage a été trouvée (la 0.4.9 n'avait clarifié que le texte) : main.js construisait l'objet de configuration passé à WashDataManager champ par champ, et « ignoreAntiKnitter » manquait entièrement dans cette liste - la protection par verrouillage ne pouvait donc jamais s'activer, quel que soit l'état de la case à cocher, même avec un motif enregistré. Deux autres réglages ont aussi été trouvés et corrigés, victimes du même type d'omission et n'ayant donc jamais d'effet : « Seuil de détection du programme (%) » (matchThreshold) et « Adoption immédiate à partir de la confiance (%) » (instantConfirmThreshold). Ajout d'un test de régression qui vérifie que chaque réglage lu par WashDataManager est bien transmis depuis main.js.",
|
|
91
|
-
"it": "Correzione: trovata la vera causa del bug del falso riavvio anti-piega (la 0.4.9 aveva solo chiarito il testo): main.js costruiva l'oggetto di configurazione passato a WashDataManager campo per campo, e \"ignoreAntiKnitter\" mancava completamente da quell'elenco - la protezione di blocco non poteva quindi mai attivarsi, indipendentemente dallo stato della casella, anche con un modello salvato. Trovate e corrette anche altre due impostazioni colpite dallo stesso tipo di omissione, mai effettive indipendentemente da cosa fosse configurato: \"Soglia di rilevamento programma (%)\" (matchThreshold) e \"Adozione istantanea dalla confidenza (%)\" (instantConfirmThreshold). Aggiunto un test di regressione che verifica che ogni impostazione letta da WashDataManager sia effettivamente passata da main.js.",
|
|
92
|
-
"es": "Corrección: se encontró la causa real del error de reinicio falso por antiarrugas (la 0.4.9 solo aclaró el texto): main.js construía el objeto de configuración pasado a WashDataManager campo por campo, y \"ignoreAntiKnitter\" faltaba por completo en esa lista - la protección de bloqueo nunca podía activarse, sin importar el estado de la casilla, incluso con un patrón guardado. También se encontraron y corrigieron otras dos configuraciones afectadas por el mismo tipo de omisión, que nunca tenían efecto sin importar lo configurado: \"Umbral de detección de programa (%)\" (matchThreshold) y \"Adopción instantánea desde confianza (%)\" (instantConfirmThreshold). Se añadió una prueba de regresión que verifica que cada ajuste leído por WashDataManager realmente se pase desde main.js.",
|
|
93
|
-
"pl": "Poprawka: znaleziono prawdziwą przyczynę błędu fałszywego restartu przy ochronie przed gnieceniem (0.4.9 doprecyzowała tylko tekst): main.js budował obiekt konfiguracji przekazywany do WashDataManager pole po polu, a \"ignoreAntiKnitter\" całkowicie brakowało na tej liście - ochrona blokady nigdy nie mogła się więc aktywować, niezależnie od stanu pola wyboru, nawet przy zapisanym wzorcu. Znaleziono i naprawiono też dwa kolejne ustawienia dotknięte tym samym rodzajem pominięcia, które nigdy nie działały niezależnie od konfiguracji: \"Próg wykrywania programu (%)\" (matchThreshold) i \"Natychmiastowe przyjęcie od pewności (%)\" (instantConfirmThreshold). Dodano test regresyjny sprawdzający, czy każde ustawienie odczytywane przez WashDataManager jest rzeczywiście przekazywane z main.js.",
|
|
94
|
-
"uk": "Виправлення: знайдено справжню причину бага фальшивого перезапуску через захист від зминання (0.4.9 лише уточнила формулювання): main.js збирав об'єкт конфігурації для WashDataManager поле за полем, і «ignoreAntiKnitter» повністю бракувало в цьому списку - тому захист блокування ніколи не міг активуватися, незалежно від стану прапорця, навіть зі збереженим шаблоном. Також знайдено й виправлено ще два налаштування, що постраждали від такого самого типу пропуску і тому ніколи не діяли, що б не було налаштовано: «Поріг розпізнавання програми (%)» (matchThreshold) і «Миттєве прийняття від впевненості (%)» (instantConfirmThreshold). Додано регресійний тест, який перевіряє, що кожне налаштування, яке читає WashDataManager, дійсно передається з main.js.",
|
|
95
|
-
"zh-cn": "修复:找到了防皱误重启问题的真正根源(0.4.9 只是澄清了措辞):main.js 在构建传给 WashDataManager 的配置对象时逐字段列出,而\"ignoreAntiKnitter\"完全没有列入其中——因此无论复选框如何设置,即使已保存参考模式,锁定保护也永远无法激活。同时还发现并修复了另外两个受到同类遗漏影响、无论如何配置都从未生效的设置:\"程序识别阈值 (%)\"(matchThreshold)和\"从置信度立即采用 (%)\"(instantConfirmThreshold)。新增了一个回归测试,用于检查 WashDataManager 读取的每一项设置是否都确实从 main.js 传递过来。"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
// Static, read-only reference data - safe as a module-level const even
|
|
7
|
+
// under compact mode (unlike per-instance mutable caches such as the
|
|
8
|
+
// notification-language cache, this never varies between instances or
|
|
9
|
+
// changes at runtime).
|
|
10
|
+
let PHASE_LABELS = {};
|
|
11
|
+
try {
|
|
12
|
+
PHASE_LABELS = JSON.parse(
|
|
13
|
+
fs.readFileSync(
|
|
14
|
+
path.join(__dirname, "..", "admin", "phaseLabels.json"),
|
|
15
|
+
"utf8",
|
|
16
|
+
),
|
|
17
|
+
);
|
|
18
|
+
} catch (_e) {
|
|
19
|
+
// Falls back to an empty table - getPhaseText() below already handles
|
|
20
|
+
// an unknown/missing phase key by returning it unchanged.
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Maps the raw `state` data point's internal values to the exact same
|
|
24
|
+
// admin/i18n/<lang>.json keys already used for this in the admin tab's
|
|
25
|
+
// device list (getStateLabel() in admin/tab_m.html) - one wording, one
|
|
26
|
+
// place it's translated, instead of a second copy that could drift.
|
|
27
|
+
const STATE_LABEL_KEYS = {
|
|
28
|
+
off: "Off",
|
|
29
|
+
starting: "Starting…",
|
|
30
|
+
running: "Running ⚙️",
|
|
31
|
+
paused: "Paused",
|
|
32
|
+
ending: "Ending…",
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Returns the localized, emoji-prefixed display text for a phase key
|
|
37
|
+
* (e.g. "washing" -> "🫧 Washing" / "🫧 Wäscht"). Falls back to the raw
|
|
38
|
+
* key itself if unknown, and to English if the given language has no
|
|
39
|
+
* translation for a known key.
|
|
40
|
+
*
|
|
41
|
+
* @param {string} phaseKey – neutral phase identifier (e.g. "washing")
|
|
42
|
+
* @param {string} lang – language code (e.g. "de"), defaults to "en"
|
|
43
|
+
* @returns {string} – e.g. "🫧 Washing"
|
|
44
|
+
*/
|
|
45
|
+
function getPhaseText(phaseKey, lang) {
|
|
46
|
+
if (!phaseKey) {
|
|
47
|
+
return "";
|
|
48
|
+
}
|
|
49
|
+
const entry = PHASE_LABELS[phaseKey];
|
|
50
|
+
if (!entry) {
|
|
51
|
+
return phaseKey;
|
|
52
|
+
}
|
|
53
|
+
const text = entry[lang || "en"] || entry.en;
|
|
54
|
+
return `${entry.emoji} ${text}`;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Returns the localized display text for a device state
|
|
59
|
+
* (off/starting/running/paused/ending), using the same admin/i18n
|
|
60
|
+
* dictionary as the rest of the admin UI (passed in already-loaded,
|
|
61
|
+
* see loadNotifTranslations() in main.js).
|
|
62
|
+
*
|
|
63
|
+
* @param {string} stateKey – off/starting/running/paused/ending
|
|
64
|
+
* @param {object} dict – translation dictionary (English key -> localized text)
|
|
65
|
+
* @returns {string} – e.g. "Running ⚙️"
|
|
66
|
+
*/
|
|
67
|
+
function getStateText(stateKey, dict) {
|
|
68
|
+
const i18nKey = STATE_LABEL_KEYS[stateKey];
|
|
69
|
+
if (!i18nKey) {
|
|
70
|
+
return stateKey || "";
|
|
71
|
+
}
|
|
72
|
+
return (dict && dict[i18nKey]) || i18nKey;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Returns the localized display text for the `program` data point.
|
|
77
|
+
* A confirmed program name is the user's own saved profile name and is
|
|
78
|
+
* passed through unchanged (it's their data, not our vocabulary to
|
|
79
|
+
* translate) - only the "detecting..." sentinel and the "≈ <name>"
|
|
80
|
+
* best-candidate prefix get localized.
|
|
81
|
+
*
|
|
82
|
+
* @param {string} rawProgram – the raw `program` data point value
|
|
83
|
+
* @param {object} dict – translation dictionary (English key -> localized text)
|
|
84
|
+
* @returns {string} – e.g. "detecting..." or the passed-through program name
|
|
85
|
+
*/
|
|
86
|
+
function getProgramText(rawProgram, dict) {
|
|
87
|
+
if (!rawProgram) {
|
|
88
|
+
return "";
|
|
89
|
+
}
|
|
90
|
+
if (rawProgram === "detecting...") {
|
|
91
|
+
return (dict && dict["detecting..."]) || rawProgram;
|
|
92
|
+
}
|
|
93
|
+
// A confirmed name (with or without the language-neutral "≈ " best-candidate
|
|
94
|
+
// prefix) is the user's own saved profile name - passed through unchanged.
|
|
95
|
+
return rawProgram;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = { getPhaseText, getStateText, getProgramText, PHASE_LABELS };
|
package/lib/washDataManager.js
CHANGED
|
@@ -19,6 +19,14 @@ const PROGRESS_RESET_MS = 5 * 60 * 1000;
|
|
|
19
19
|
const STUCK_POWER_MS = 10 * 60 * 1000;
|
|
20
20
|
const MIN_CYCLE_MS = 2 * 60 * 1000;
|
|
21
21
|
const MIN_CONFIDENCE_FOR_SET = 0.6; // Minimum average score to set a program via accumulation - deliberately strict (better "detecting..." than a wrong match)
|
|
22
|
+
// Dryer anti-crease lock: how far a genuine anti-crease tumble spike pushes
|
|
23
|
+
// the lock's expiry forward, and the absolute ceiling (from when the lock
|
|
24
|
+
// was first set) that extensions can never exceed - real anti-crease
|
|
25
|
+
// duration varies cycle to cycle (load size, dampness), so a purely fixed
|
|
26
|
+
// lock length (learned duration + buffer) can run out while the dryer is
|
|
27
|
+
// still actually tumbling, letting the tail end be mistaken for a new cycle.
|
|
28
|
+
const ANTI_CREASE_LOCK_REFRESH_MS = 10 * 60 * 1000;
|
|
29
|
+
const ANTI_CREASE_LOCK_MAX_MS = 90 * 60 * 1000;
|
|
22
30
|
|
|
23
31
|
/**
|
|
24
32
|
* Central orchestrator: wires the CycleDetector, ProfileStore, and
|
|
@@ -75,6 +83,7 @@ class WashDataManager {
|
|
|
75
83
|
// Dryer Anti-Knitter
|
|
76
84
|
this._dryerDropTriggered = false;
|
|
77
85
|
this._dryerLockUntil = null;
|
|
86
|
+
this._dryerLockStartedAt = null; // when the lock was first set - for the absolute ceiling
|
|
78
87
|
this._dryerHighStart = null;
|
|
79
88
|
this._antiKnitter = null; // { maxWatts, durationMs } – loaded from profileStore
|
|
80
89
|
}
|
|
@@ -307,6 +316,7 @@ class WashDataManager {
|
|
|
307
316
|
`${this._name}: anti-crease protection inactive (disabled or no pattern) – existing lock discarded`,
|
|
308
317
|
);
|
|
309
318
|
this._dryerLockUntil = null;
|
|
319
|
+
this._dryerLockStartedAt = null;
|
|
310
320
|
this._dryerDropTriggered = false;
|
|
311
321
|
this._dryerHighStart = null;
|
|
312
322
|
}
|
|
@@ -317,6 +327,7 @@ class WashDataManager {
|
|
|
317
327
|
`${this._name}: anti-crease lock period expired`,
|
|
318
328
|
);
|
|
319
329
|
this._dryerLockUntil = null;
|
|
330
|
+
this._dryerLockStartedAt = null;
|
|
320
331
|
this._dryerDropTriggered = false;
|
|
321
332
|
this._dryerHighStart = null;
|
|
322
333
|
} else {
|
|
@@ -340,6 +351,7 @@ class WashDataManager {
|
|
|
340
351
|
`${this._name}: real new cycle detected after the beep (>${realThreshold}W for 30s) – lock lifted`,
|
|
341
352
|
);
|
|
342
353
|
this._dryerLockUntil = null;
|
|
354
|
+
this._dryerLockStartedAt = null;
|
|
343
355
|
this._dryerDropTriggered = false;
|
|
344
356
|
this._dryerHighStart = null;
|
|
345
357
|
// Continue with normal processing
|
|
@@ -353,6 +365,23 @@ class WashDataManager {
|
|
|
353
365
|
);
|
|
354
366
|
}
|
|
355
367
|
this._dryerHighStart = null;
|
|
368
|
+
// A meaningful spike (motor tumble, not just idle standby noise)
|
|
369
|
+
// is itself evidence the dryer is still actually doing its
|
|
370
|
+
// anti-crease thing - push the lock's expiry forward instead of
|
|
371
|
+
// letting a fixed-length lock run out mid-tumble, capped at an
|
|
372
|
+
// absolute ceiling from when the lock was first set.
|
|
373
|
+
if (watts > 10 && this._dryerLockStartedAt) {
|
|
374
|
+
const extended = Math.min(
|
|
375
|
+
ts + ANTI_CREASE_LOCK_REFRESH_MS,
|
|
376
|
+
this._dryerLockStartedAt + ANTI_CREASE_LOCK_MAX_MS,
|
|
377
|
+
);
|
|
378
|
+
if (extended > this._dryerLockUntil) {
|
|
379
|
+
this.adapter.log.debug(
|
|
380
|
+
`${this._name}: anti-crease still active (${watts.toFixed(0)}W) – lock extended to ${Math.round((extended - ts) / 60000)} min from now`,
|
|
381
|
+
);
|
|
382
|
+
this._dryerLockUntil = extended;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
356
385
|
this.adapter.log.debug(
|
|
357
386
|
`${this._name}: anti-crease spike ignored (${watts.toFixed(0)}W < ${realThreshold}W threshold)`,
|
|
358
387
|
);
|
|
@@ -394,6 +423,7 @@ class WashDataManager {
|
|
|
394
423
|
);
|
|
395
424
|
this._dryerDropTriggered = true;
|
|
396
425
|
this._dryerLockUntil = ts + lockMs;
|
|
426
|
+
this._dryerLockStartedAt = ts;
|
|
397
427
|
this._dryerCooldownEnd = Date.now() + 45000;
|
|
398
428
|
// Wait 45 seconds – trace keeps recording for a nicer-looking graph
|
|
399
429
|
// Detector stays active so further points get collected
|
|
@@ -1524,6 +1554,7 @@ class WashDataManager {
|
|
|
1524
1554
|
// Also immediately lift any currently active lock period, so the
|
|
1525
1555
|
// next cycle can be recorded without interference.
|
|
1526
1556
|
this._dryerLockUntil = null;
|
|
1557
|
+
this._dryerLockStartedAt = null;
|
|
1527
1558
|
this._dryerDropTriggered = false;
|
|
1528
1559
|
this._dryerHighStart = null;
|
|
1529
1560
|
this.adapter.log.info(`${this._name}: anti-crease pattern deleted`);
|
package/main.js
CHANGED
|
@@ -5,6 +5,11 @@ const fs = require("node:fs");
|
|
|
5
5
|
const path = require("node:path");
|
|
6
6
|
const { WashDataManager } = require("./lib/washDataManager");
|
|
7
7
|
const { resolveStatePlaceholders } = require("./lib/notifyTemplate");
|
|
8
|
+
const {
|
|
9
|
+
getPhaseText,
|
|
10
|
+
getStateText,
|
|
11
|
+
getProgramText,
|
|
12
|
+
} = require("./lib/displayLabels");
|
|
8
13
|
|
|
9
14
|
// ── Notification message translations ──────────────────────────
|
|
10
15
|
// Reuses the same admin/i18n/<lang>.json files as the admin UI, so the
|
|
@@ -198,6 +203,9 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
198
203
|
: 55,
|
|
199
204
|
notifyOnProbable: !!src.notifyOnProbable,
|
|
200
205
|
ignoreAntiKnitter: src.ignoreAntiKnitter !== false,
|
|
206
|
+
// "" (the default) means "use the ioBroker system language" -
|
|
207
|
+
// resolved once at startup, see onReady()'s _deviceLangCache setup.
|
|
208
|
+
displayLanguage: src.displayLanguage || "",
|
|
201
209
|
};
|
|
202
210
|
}
|
|
203
211
|
|
|
@@ -243,6 +251,11 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
243
251
|
async onReady() {
|
|
244
252
|
this.setState("info.connection", false, true);
|
|
245
253
|
|
|
254
|
+
// Resolve and cache the system language early (not just lazily on the
|
|
255
|
+
// first notification) - the phaseText/stateText/programText data
|
|
256
|
+
// points need it available as soon as the first status update comes in.
|
|
257
|
+
await loadNotifTranslations(this);
|
|
258
|
+
|
|
246
259
|
await this.setObjectNotExistsAsync("files", {
|
|
247
260
|
type: "meta",
|
|
248
261
|
common: { name: "LaundryLens files", type: "meta.folder" },
|
|
@@ -299,9 +312,14 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
299
312
|
return;
|
|
300
313
|
}
|
|
301
314
|
|
|
315
|
+
this._deviceLangCache = {};
|
|
302
316
|
for (const deviceCfg of deviceList) {
|
|
303
317
|
await this._createDeviceObjects(deviceCfg);
|
|
304
318
|
await this._migrateStateNames(deviceCfg.deviceId);
|
|
319
|
+
// Resolved once here (not on every status update) since it can't
|
|
320
|
+
// change without an instance restart anyway.
|
|
321
|
+
this._deviceLangCache[deviceCfg.deviceId] =
|
|
322
|
+
deviceCfg.displayLanguage || this._notifLang || "en";
|
|
305
323
|
|
|
306
324
|
const manager = new WashDataManager(
|
|
307
325
|
this,
|
|
@@ -444,12 +462,17 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
444
462
|
100,
|
|
445
463
|
Math.round((elapsed / profile.durationMs) * 100),
|
|
446
464
|
);
|
|
447
|
-
//
|
|
465
|
+
// Set data points directly
|
|
448
466
|
await this.setStateAsync(
|
|
449
467
|
`${deviceCfg.deviceId}.state`,
|
|
450
468
|
"running",
|
|
451
469
|
true,
|
|
452
470
|
);
|
|
471
|
+
await this.setStateAsync(
|
|
472
|
+
`${deviceCfg.deviceId}.stateText`,
|
|
473
|
+
getStateText("running", this._notifTranslations),
|
|
474
|
+
true,
|
|
475
|
+
);
|
|
453
476
|
await this.setStateAsync(
|
|
454
477
|
`${deviceCfg.deviceId}.running`,
|
|
455
478
|
true,
|
|
@@ -460,6 +483,14 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
460
483
|
manager.currentProgram.name,
|
|
461
484
|
true,
|
|
462
485
|
);
|
|
486
|
+
await this.setStateAsync(
|
|
487
|
+
`${deviceCfg.deviceId}.programText`,
|
|
488
|
+
getProgramText(
|
|
489
|
+
manager.currentProgram.name,
|
|
490
|
+
this._notifTranslations,
|
|
491
|
+
),
|
|
492
|
+
true,
|
|
493
|
+
);
|
|
463
494
|
await this.setStateAsync(
|
|
464
495
|
`${deviceCfg.deviceId}.confidence`,
|
|
465
496
|
Math.round(manager.confidence * 100),
|
|
@@ -1283,8 +1314,15 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1283
1314
|
const prevState = this._lastState && this._lastState[deviceId];
|
|
1284
1315
|
this._lastState = this._lastState || {};
|
|
1285
1316
|
this._lastState[deviceId] = status.state;
|
|
1317
|
+
const lang =
|
|
1318
|
+
(this._deviceLangCache && this._deviceLangCache[deviceId]) || "en";
|
|
1286
1319
|
|
|
1287
1320
|
this.setState(`${deviceId}.state`, status.state, true);
|
|
1321
|
+
this.setState(
|
|
1322
|
+
`${deviceId}.stateText`,
|
|
1323
|
+
getStateText(status.state, this._notifTranslations),
|
|
1324
|
+
true,
|
|
1325
|
+
);
|
|
1288
1326
|
this.setState(`${deviceId}.running`, status.running, true);
|
|
1289
1327
|
|
|
1290
1328
|
// Keep confidence + program data point always up to date
|
|
@@ -1294,27 +1332,36 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1294
1332
|
status.bestCandidate.confidence,
|
|
1295
1333
|
true,
|
|
1296
1334
|
);
|
|
1335
|
+
const bestCandidateProgram = `≈ ${status.bestCandidate.name}`;
|
|
1336
|
+
this.setState(`${deviceId}.program`, bestCandidateProgram, true);
|
|
1297
1337
|
this.setState(
|
|
1298
|
-
`${deviceId}.
|
|
1299
|
-
|
|
1338
|
+
`${deviceId}.programText`,
|
|
1339
|
+
getProgramText(bestCandidateProgram, this._notifTranslations),
|
|
1300
1340
|
true,
|
|
1301
1341
|
);
|
|
1302
1342
|
} else if (status.program && status.program !== "detecting...") {
|
|
1303
1343
|
// Already set by _onProgram, but kept as a safety net
|
|
1304
1344
|
} else if (status.state === "off") {
|
|
1305
1345
|
this.setState(`${deviceId}.program`, "", true);
|
|
1346
|
+
this.setState(`${deviceId}.programText`, "", true);
|
|
1306
1347
|
this.setState(`${deviceId}.confidence`, 0, true);
|
|
1307
1348
|
}
|
|
1308
1349
|
|
|
1309
1350
|
// Write phase as data point (without emoji)
|
|
1310
1351
|
// Only show phase while the device is actively running
|
|
1311
1352
|
if (status.phase && status.state === "running") {
|
|
1312
|
-
const
|
|
1353
|
+
const phaseKey = status.phase
|
|
1313
1354
|
.replace(/^[\u{1F300}-\u{1FFFF}\u{2600}-\u{27BF}]\s*/u, "")
|
|
1314
1355
|
.trim();
|
|
1315
|
-
this.setState(`${deviceId}.phase`,
|
|
1356
|
+
this.setState(`${deviceId}.phase`, phaseKey, true);
|
|
1357
|
+
this.setState(
|
|
1358
|
+
`${deviceId}.phaseText`,
|
|
1359
|
+
getPhaseText(phaseKey, lang),
|
|
1360
|
+
true,
|
|
1361
|
+
);
|
|
1316
1362
|
} else if (status.state === "off" || status.state === "ending") {
|
|
1317
1363
|
this.setState(`${deviceId}.phase`, "", true);
|
|
1364
|
+
this.setState(`${deviceId}.phaseText`, "", true);
|
|
1318
1365
|
}
|
|
1319
1366
|
|
|
1320
1367
|
// Send start notification on transition to running
|
|
@@ -1334,6 +1381,11 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1334
1381
|
this._lastProgram[deviceId] = program;
|
|
1335
1382
|
|
|
1336
1383
|
this.setState(`${deviceId}.program`, program, true);
|
|
1384
|
+
this.setState(
|
|
1385
|
+
`${deviceId}.programText`,
|
|
1386
|
+
getProgramText(program, this._notifTranslations),
|
|
1387
|
+
true,
|
|
1388
|
+
);
|
|
1337
1389
|
this.setState(`${deviceId}.confidence`, Math.round(confidence * 100), true);
|
|
1338
1390
|
|
|
1339
1391
|
// Update message on transition detecting → program:
|
|
@@ -2028,6 +2080,30 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
2028
2080
|
def: "",
|
|
2029
2081
|
write: false,
|
|
2030
2082
|
},
|
|
2083
|
+
{
|
|
2084
|
+
id: "phaseText",
|
|
2085
|
+
name: "Current phase (localized)",
|
|
2086
|
+
type: "string",
|
|
2087
|
+
role: "text",
|
|
2088
|
+
def: "",
|
|
2089
|
+
write: false,
|
|
2090
|
+
},
|
|
2091
|
+
{
|
|
2092
|
+
id: "stateText",
|
|
2093
|
+
name: "Status (localized)",
|
|
2094
|
+
type: "string",
|
|
2095
|
+
role: "text",
|
|
2096
|
+
def: "",
|
|
2097
|
+
write: false,
|
|
2098
|
+
},
|
|
2099
|
+
{
|
|
2100
|
+
id: "programText",
|
|
2101
|
+
name: "Detected program (localized)",
|
|
2102
|
+
type: "string",
|
|
2103
|
+
role: "text",
|
|
2104
|
+
def: "",
|
|
2105
|
+
write: false,
|
|
2106
|
+
},
|
|
2031
2107
|
{
|
|
2032
2108
|
id: "lastMessage",
|
|
2033
2109
|
name: "Last notification",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.laundrylens",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.18",
|
|
4
4
|
"description": "ioBroker adapter that detects washing machine and dryer cycles via smart plug power measurement, matches them against learned programs and estimates remaining time",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "backfisch88",
|