iobroker.laundrylens 0.4.32 → 0.4.33
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 +8 -0
- package/admin/i18n/de.json +5 -1
- package/admin/i18n/en.json +5 -1
- package/admin/i18n/es.json +5 -1
- package/admin/i18n/fr.json +5 -1
- package/admin/i18n/it.json +5 -1
- package/admin/i18n/nl.json +5 -1
- package/admin/i18n/pl.json +5 -1
- package/admin/i18n/pt.json +5 -1
- package/admin/i18n/ru.json +5 -1
- package/admin/i18n/uk.json +5 -1
- package/admin/i18n/zh-cn.json +5 -1
- package/admin/jsonConfig.json +362 -304
- package/io-package.json +14 -14
- package/lib/displayLabels.js +23 -10
- package/lib/washDataManager.js +68 -2
- package/main.js +31 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,6 +132,14 @@ This also works inside the conditional `[...]` blocks: `[🌡️ Outside: {state
|
|
|
132
132
|
|
|
133
133
|
### **WORK IN PROGRESS**
|
|
134
134
|
|
|
135
|
+
### 0.4.33 (2026-10-02)
|
|
136
|
+
- Fix: reported live with logs + a screenshot - a brand new cycle could show a leftover best-candidate guess from the *previous*, already-finished cycle for several minutes (e.g. "~30 Speed (76%)" right at the start of a new washing-machine cycle, matching exactly what the previous cycle had matched), before that new cycle's own matching had run even once. Root cause: `_onDetectorState()`'s `STARTING` case reset about a dozen per-cycle fields but not `_bestCandidate`, the field driving both the admin tab's live preview and, at the next confirmed transition, the persisted `program`/`programText`/`confidence` data points. The `STARTING` transition is now the single, authoritative reset point for every per-cycle field (also added the phase-tracking fields as defensive redundancy) - a new cycle can no longer show anything left over from the one before it
|
|
137
|
+
- Fix: `cycleProgress` stayed stuck at its last value (often 100%) after a cycle finished, instead of resetting to 0. The reset call reused `_onTime()`, which has a guard that only writes `cycleProgress` when `progressPct > 0` (meant to ignore a brief non-match mid-cycle) - the deliberate reset-to-0 call was silently swallowed by that same guard. `_onTime()` now takes a `forceWrite` parameter that the reset call uses to bypass the guard
|
|
138
|
+
- Fix: an uncaught exception during cycle-finish post-processing (notably the post-hoc phase analysis that only washing machines/dishwashers run, more complex than the dryer's live phase tracking) could silently skip the `onStateChange` callback that keeps the `state`/`stateText`/`running`/`program`/`phase` data points in sync - the same class of bug as 0.4.31's dryer-specific case, but for any device type and any exception source. Now guarded with try/catch so a failure in post-processing can never block the state-sync callback
|
|
139
|
+
- New option **"Show probable program before confirmation"**: off by default (unchanged behavior - `program`/`programText` show `"detecting..."` until a match is actually confirmed). When enabled, a live best-candidate guess reaching at least 40% confidence is shown immediately, prefixed with `"≈"` (e.g. `"≈ 30 Speed"`), updated continuously as confidence changes - not just the admin tab's own preview anymore, but the actual data points too
|
|
140
|
+
- New option **"Text data points without emoji"**: off by default. When enabled, `phaseText`/`stateText` omit their emoji (e.g. `"Washing"` instead of `"🫧 Washing"`) - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully
|
|
141
|
+
- All four issues found via a live report (screenshots + debug logs) and fixed with full regression test coverage, including reverting each fix and confirming its test fails first
|
|
142
|
+
|
|
135
143
|
### 0.4.32 (2026-09-27)
|
|
136
144
|
- Fix: follow-up to 0.4.31 - a restart alone did not clear an *already*-stale `state` data point (e.g. stuck on "Running"), even with 0.4.31 installed. `onReady()` only re-wrote the `state`/`stateText`/`running`/`program`/`phase` data points when a restore branch fired (sensor currently drawing power, or a saved cycle needs resuming/finishing) - with neither true (the common case: an idle device whose last cycle already finished cleanly), nothing touched them at all, so a stale leftover value would survive any number of restarts
|
|
137
145
|
- Fixed by unconditionally re-syncing these data points with the manager's actual resolved state (`manager.currentState`/`manager.getStatus()`) at the end of every device's startup, regardless of which restore branch fired or didn't. Covered by a new source-inspection regression test (`tests/test_startup_state_sync.js`); also re-ran the full js-controller integration test since this touches `onReady()`
|
package/admin/i18n/de.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} fertig!\n⏱️ Gesamtlaufzeit: {duration} min\n📊 Programm: {program}\n⚡ Verbrauch: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} läuft\n⏳ Zeit wird ermittelt…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} Update\n✅ Fertig um {endTime} Uhr\n[↩️ Vorher: {prevTime}]\n📊 Programm: {program}\n📈 Fortschritt: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} Update\n✅ Fertig um {endTime} Uhr\n📊 Programm: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} Update\n✅ Fertig um {endTime} Uhr\n📊 Programm: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Wahrscheinliches Programm vor Bestätigung anzeigen",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "AUS (Standard): Die Datenpunkte program/programText zeigen \"detecting...\", bis ein Treffer tatsächlich bestätigt ist. EIN: Sobald eine Live-Vermutung mindestens 40% Konfidenz erreicht, wird sie sofort mit dem Präfix \"≈\" angezeigt (z. B. \"≈ 30 Speed\") - fortlaufend aktualisiert, nicht erst nach Bestätigung.",
|
|
219
|
+
"Text data points without emoji": "Text-Datenpunkte ohne Emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "AUS (Standard): Die Datenpunkte phaseText/stateText enthalten ein voran-/nachgestelltes Emoji (z. B. \"🫧 Washing\", \"Läuft ⚙️\"). EIN: Das Emoji wird weggelassen - nützlich für Dashboards, Sprachausgabe oder überall, wo Emoji nicht sinnvoll dargestellt werden."
|
|
217
221
|
}
|
package/admin/i18n/en.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} running\n⏳ Determining time…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Show probable program before confirmation",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.",
|
|
219
|
+
"Text data points without emoji": "Text data points without emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully."
|
|
217
221
|
}
|
package/admin/i18n/es.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} listo!\n⏱️ Duración total: {duration} min\n📊 Programa: {program}\n⚡ Consumo: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} en marcha\n⏳ Determinando el tiempo…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} actualización\n✅ Listo a las {endTime}\n[↩️ Antes: {prevTime}]\n📊 Programa: {program}\n📈 Progreso: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} actualización\n✅ Listo a las {endTime}\n📊 Programa: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} actualización\n✅ Listo a las {endTime}\n📊 Programa: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Mostrar programa probable antes de la confirmación",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "APAGADO (predeterminado): los data points program/programText muestran \"detecting...\" hasta que una coincidencia se confirma realmente. ENCENDIDO: en cuanto una estimación en vivo del mejor candidato alcanza al menos un 40% de confianza, se muestra de inmediato, precedida de \"≈\" (p. ej. \"≈ 30 Speed\") - actualizada continuamente a medida que cambia la confianza, no solo una vez confirmada.",
|
|
219
|
+
"Text data points without emoji": "Data points de texto sin emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "APAGADO (predeterminado): los data points phaseText/stateText incluyen un emoji al inicio/final (p. ej. \"🫧 Washing\", \"Running ⚙️\"). ENCENDIDO: se omite el emoji - útil para paneles, texto a voz, o donde los emoji no se muestren de forma útil."
|
|
217
221
|
}
|
package/admin/i18n/fr.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} terminé !\n⏱️ Durée totale : {duration} min\n📊 Programme : {program}\n⚡ Consommation : {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} en marche\n⏳ Détermination du temps…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} mise à jour\n✅ Terminé à {endTime}\n[↩️ Avant : {prevTime}]\n📊 Programme : {program}\n📈 Progression : {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} mise à jour\n✅ Terminé à {endTime}\n📊 Programme : {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} mise à jour\n✅ Terminé à {endTime}\n📊 Programme : {program}",
|
|
217
|
+
"Show probable program before confirmation": "Afficher le programme probable avant confirmation",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "DÉSACTIVÉ (par défaut) : les points de données program/programText affichent \"detecting...\" jusqu'à ce qu'une correspondance soit réellement confirmée. ACTIVÉ : dès qu'une estimation du meilleur candidat en direct atteint au moins 40 % de confiance, elle est affichée immédiatement, précédée de \"≈\" (ex. \"≈ 30 Speed\") - mise à jour en continu à mesure que la confiance évolue, pas seulement une fois confirmée.",
|
|
219
|
+
"Text data points without emoji": "Points de données texte sans emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "DÉSACTIVÉ (par défaut) : les points de données phaseText/stateText incluent un emoji en début/fin (ex. \"🫧 Washing\", \"Running ⚙️\"). ACTIVÉ : l'emoji est omis - utile pour les tableaux de bord, la synthèse vocale, ou partout où les emoji ne s'affichent pas utilement."
|
|
217
221
|
}
|
package/admin/i18n/it.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} completato!\n⏱️ Durata totale: {duration} min\n📊 Programma: {program}\n⚡ Consumo: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} in funzione\n⏳ Determinazione del tempo…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} aggiornamento\n✅ Completato alle {endTime}\n[↩️ Prima: {prevTime}]\n📊 Programma: {program}\n📈 Avanzamento: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} aggiornamento\n✅ Completato alle {endTime}\n📊 Programma: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} aggiornamento\n✅ Completato alle {endTime}\n📊 Programma: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Mostra il programma probabile prima della conferma",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "OFF (predefinito): i data point program/programText mostrano \"detecting...\" finché una corrispondenza non viene effettivamente confermata. ON: non appena una stima del miglior candidato in tempo reale raggiunge almeno il 40% di confidenza, viene mostrata subito, preceduta da \"≈\" (es. \"≈ 30 Speed\") - aggiornata continuamente al variare della confidenza, non solo una volta confermata.",
|
|
219
|
+
"Text data points without emoji": "Data point di testo senza emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "OFF (predefinito): i data point phaseText/stateText includono un'emoji iniziale/finale (es. \"🫧 Washing\", \"Running ⚙️\"). ON: l'emoji viene omessa - utile per dashboard, sintesi vocale, o ovunque le emoji non vengano rese in modo utile."
|
|
217
221
|
}
|
package/admin/i18n/nl.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} klaar!\n⏱️ Totale looptijd: {duration} min\n📊 Programma: {program}\n⚡ Verbruik: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} actief\n⏳ Tijd wordt bepaald…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} update\n✅ Klaar om {endTime}\n[↩️ Voorheen: {prevTime}]\n📊 Programma: {program}\n📈 Voortgang: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} update\n✅ Klaar om {endTime}\n📊 Programma: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} update\n✅ Klaar om {endTime}\n📊 Programma: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Waarschijnlijk programma tonen voor bevestiging",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "UIT (standaard): de datapoints program/programText tonen \"detecting...\" totdat een match daadwerkelijk is bevestigd. AAN: zodra een live beste-kandidaat-gok minstens 40% betrouwbaarheid bereikt, wordt deze direct getoond, voorafgegaan door \"≈\" (bijv. \"≈ 30 Speed\") - continu bijgewerkt naarmate de betrouwbaarheid verandert, niet pas na bevestiging.",
|
|
219
|
+
"Text data points without emoji": "Tekst-datapoints zonder emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "UIT (standaard): de datapoints phaseText/stateText bevatten een emoji aan het begin/einde (bijv. \"🫧 Washing\", \"Running ⚙️\"). AAN: de emoji wordt weggelaten - handig voor dashboards, tekst-naar-spraak, of overal waar emoji niet nuttig worden weergegeven."
|
|
217
221
|
}
|
package/admin/i18n/pl.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} gotowe!\n⏱️ Całkowity czas: {duration} min\n📊 Program: {program}\n⚡ Zużycie: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} działa\n⏳ Określanie czasu…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} aktualizacja\n✅ Gotowe o {endTime}\n[↩️ Wcześniej: {prevTime}]\n📊 Program: {program}\n📈 Postęp: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} aktualizacja\n✅ Gotowe o {endTime}\n📊 Program: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} aktualizacja\n✅ Gotowe o {endTime}\n📊 Program: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Pokaż prawdopodobny program przed potwierdzeniem",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "WYŁ. (domyślnie): datapointy program/programText pokazują \"detecting...\", dopóki dopasowanie nie zostanie faktycznie potwierdzone. WŁ.: gdy tylko aktualne przypuszczenie najlepszego kandydata osiągnie co najmniej 40% pewności, jest natychmiast pokazywane z przedrostkiem \"≈\" (np. \"≈ 30 Speed\") - aktualizowane na bieżąco wraz ze zmianą pewności, a nie dopiero po potwierdzeniu.",
|
|
219
|
+
"Text data points without emoji": "Datapointy tekstowe bez emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "WYŁ. (domyślnie): datapointy phaseText/stateText zawierają emoji na początku/końcu (np. \"🫧 Washing\", \"Running ⚙️\"). WŁ.: emoji jest pomijane - przydatne w dashboardach, syntezie mowy lub wszędzie tam, gdzie emoji nie są sensownie wyświetlane."
|
|
217
221
|
}
|
package/admin/i18n/pt.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} concluído!\n⏱️ Duração total: {duration} min\n📊 Programa: {program}\n⚡ Consumo: {energy} kWh",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} em funcionamento\n⏳ Determinando o tempo…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} atualização\n✅ Concluído às {endTime}\n[↩️ Antes: {prevTime}]\n📊 Programa: {program}\n📈 Progresso: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} atualização\n✅ Concluído às {endTime}\n📊 Programa: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} atualização\n✅ Concluído às {endTime}\n📊 Programa: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Mostrar programa provável antes da confirmação",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "DESLIGADO (padrão): os data points program/programText mostram \"detecting...\" até que uma correspondência seja realmente confirmada. LIGADO: assim que um palpite de melhor candidato ao vivo atingir ao menos 40% de confiança, ele é mostrado imediatamente, prefixado com \"≈\" (ex. \"≈ 30 Speed\") - atualizado continuamente conforme a confiança muda, não apenas quando confirmado.",
|
|
219
|
+
"Text data points without emoji": "Data points de texto sem emoji",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "DESLIGADO (padrão): os data points phaseText/stateText incluem um emoji no início/fim (ex. \"🫧 Washing\", \"Running ⚙️\"). LIGADO: o emoji é omitido - útil para painéis, texto para fala, ou onde os emojis não sejam renderizados de forma útil."
|
|
217
221
|
}
|
package/admin/i18n/ru.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} готово!\n⏱️ Общее время: {duration} мин\n📊 Программа: {program}\n⚡ Потребление: {energy} кВтч",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} работает\n⏳ Определение времени…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} обновление\n✅ Готово в {endTime}\n[↩️ Ранее: {prevTime}]\n📊 Программа: {program}\n📈 Прогресс: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} обновление\n✅ Готово в {endTime}\n📊 Программа: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} обновление\n✅ Готово в {endTime}\n📊 Программа: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Показывать вероятную программу до подтверждения",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "ВЫКЛ (по умолчанию): датапоинты program/programText показывают \"detecting...\", пока совпадение не подтверждено. ВКЛ: как только текущая наиболее вероятная догадка достигает минимум 40% уверенности, она сразу показывается с префиксом \"≈\" (например, \"≈ 30 Speed\") - обновляется непрерывно, а не только после подтверждения.",
|
|
219
|
+
"Text data points without emoji": "Текстовые датапоинты без эмодзи",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "ВЫКЛ (по умолчанию): датапоинты phaseText/stateText содержат эмодзи в начале/конце (например, \"🫧 Washing\", \"Running ⚙️\"). ВКЛ: эмодзи опускается - полезно для дашбордов, синтеза речи или везде, где эмодзи не отображаются корректно."
|
|
217
221
|
}
|
package/admin/i18n/uk.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} готово!\n⏱️ Загальний час: {duration} хв\n📊 Програма: {program}\n⚡ Споживання: {energy} кВтгод",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} працює\n⏳ Визначення часу…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} оновлення\n✅ Готово о {endTime}\n[↩️ Раніше: {prevTime}]\n📊 Програма: {program}\n📈 Прогрес: {progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} оновлення\n✅ Готово о {endTime}\n📊 Програма: {program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} оновлення\n✅ Готово о {endTime}\n📊 Програма: {program}",
|
|
217
|
+
"Show probable program before confirmation": "Показувати ймовірну програму до підтвердження",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "ВИМК (за замовчуванням): датапоінти program/programText показують \"detecting...\", поки збіг не буде дійсно підтверджено. УВІМК: щойно поточне припущення найкращого кандидата досягає принаймні 40% впевненості, воно одразу показується з префіксом \"≈\" (наприклад, \"≈ 30 Speed\") - оновлюється безперервно зі зміною впевненості, а не лише після підтвердження.",
|
|
219
|
+
"Text data points without emoji": "Текстові датапоінти без емодзі",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "ВИМК (за замовчуванням): датапоінти phaseText/stateText містять емодзі на початку/в кінці (наприклад, \"🫧 Washing\", \"Running ⚙️\"). УВІМК: емодзі опускається - корисно для дашбордів, синтезу мовлення або будь-де, де емодзі не відображаються коректно."
|
|
217
221
|
}
|
package/admin/i18n/zh-cn.json
CHANGED
|
@@ -213,5 +213,9 @@
|
|
|
213
213
|
"🧺 {device} done!\n⏱️ Total runtime: {duration} min\n📊 Program: {program}\n⚡ Consumption: {energy} kWh": "🧺 {device} 完成!\n⏱️ 总运行时间:{duration} 分钟\n📊 程序:{program}\n⚡ 消耗:{energy} 千瓦时",
|
|
214
214
|
"🧺 {device} running\n⏳ Determining time…": "🧺 {device} 运行中\n⏳ 正在确定时间…",
|
|
215
215
|
"🧺 {device} update\n✅ Done at {endTime}\n[↩️ Before: {prevTime}]\n📊 Program: {program}\n📈 Progress: {progress}%": "🧺 {device} 更新\n✅ 完成于 {endTime}\n[↩️ 之前:{prevTime}]\n📊 程序:{program}\n📈 进度:{progress}%",
|
|
216
|
-
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} 更新\n✅ 完成于 {endTime}\n📊 程序:{program}"
|
|
216
|
+
"🧺 {device} update\n✅ Done at {endTime}\n📊 Program: {program}": "🧺 {device} 更新\n✅ 完成于 {endTime}\n📊 程序:{program}",
|
|
217
|
+
"Show probable program before confirmation": "确认前显示可能的程序",
|
|
218
|
+
"OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.": "关闭(默认):在真正确认匹配之前,program/programText 数据点显示「detecting...」。开启:一旦实时的最佳候选猜测达到至少 40% 的置信度,就会立即显示,并带有「≈」前缀(例如「≈ 30 Speed」)——随着置信度变化持续更新,而不仅仅是在确认后才显示。",
|
|
219
|
+
"Text data points without emoji": "文本数据点不含表情符号",
|
|
220
|
+
"OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.": "关闭(默认):phaseText/stateText 数据点包含前置/后置表情符号(例如「🫧 Washing」「Running ⚙️」)。开启:省略表情符号——适用于仪表盘、文本转语音,或任何表情符号无法正常显示的场景。"
|
|
217
221
|
}
|
package/admin/jsonConfig.json
CHANGED
|
@@ -1,312 +1,370 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
},
|
|
17
|
-
"deviceType": {
|
|
18
|
-
"type": "select",
|
|
19
|
-
"label": "Device type",
|
|
20
|
-
"default": "washing_machine",
|
|
21
|
-
"options": [
|
|
22
|
-
{
|
|
23
|
-
"value": "washing_machine",
|
|
24
|
-
"label": "🧺 Washing machine"
|
|
2
|
+
"i18n": true,
|
|
3
|
+
"type": "panel",
|
|
4
|
+
"items": {
|
|
5
|
+
"name": {
|
|
6
|
+
"type": "text",
|
|
7
|
+
"label": "Device name",
|
|
8
|
+
"placeholder": "e.g. Washing machine",
|
|
9
|
+
"default": "Washing machine",
|
|
10
|
+
"help": "Display name in the dashboard",
|
|
11
|
+
"xs": 12,
|
|
12
|
+
"sm": 12,
|
|
13
|
+
"md": 6,
|
|
14
|
+
"lg": 4,
|
|
15
|
+
"xl": 4
|
|
25
16
|
},
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
17
|
+
"deviceType": {
|
|
18
|
+
"type": "select",
|
|
19
|
+
"label": "Device type",
|
|
20
|
+
"default": "washing_machine",
|
|
21
|
+
"options": [
|
|
22
|
+
{
|
|
23
|
+
"value": "washing_machine",
|
|
24
|
+
"label": "🧺 Washing machine"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"value": "dishwasher",
|
|
28
|
+
"label": "🍽️ Dishwasher"
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
"value": "dryer",
|
|
32
|
+
"label": "♨️ Dryer"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"value": "washer_dryer",
|
|
36
|
+
"label": "🔄 Washer-dryer"
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
"value": "other",
|
|
40
|
+
"label": "⚙️ Other"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"xs": 12,
|
|
44
|
+
"sm": 12,
|
|
45
|
+
"md": 6,
|
|
46
|
+
"lg": 4,
|
|
47
|
+
"xl": 4
|
|
29
48
|
},
|
|
30
|
-
{
|
|
31
|
-
|
|
32
|
-
|
|
49
|
+
"powerId": {
|
|
50
|
+
"type": "objectId",
|
|
51
|
+
"label": "Power sensor (watt)",
|
|
52
|
+
"help": "ioBroker data point providing the current power draw in watts",
|
|
53
|
+
"xs": 12,
|
|
54
|
+
"sm": 12,
|
|
55
|
+
"md": 12,
|
|
56
|
+
"lg": 6,
|
|
57
|
+
"xl": 6
|
|
33
58
|
},
|
|
34
|
-
{
|
|
35
|
-
|
|
36
|
-
|
|
59
|
+
"_sep1": {
|
|
60
|
+
"type": "divider",
|
|
61
|
+
"xs": 12,
|
|
62
|
+
"sm": 12,
|
|
63
|
+
"md": 12,
|
|
64
|
+
"lg": 12,
|
|
65
|
+
"xl": 12
|
|
37
66
|
},
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
67
|
+
"_hdr1": {
|
|
68
|
+
"type": "staticText",
|
|
69
|
+
"text": "Detection thresholds",
|
|
70
|
+
"style": {
|
|
71
|
+
"fontWeight": "bold",
|
|
72
|
+
"marginTop": "8px"
|
|
73
|
+
},
|
|
74
|
+
"xs": 12,
|
|
75
|
+
"sm": 12,
|
|
76
|
+
"md": 12,
|
|
77
|
+
"lg": 12,
|
|
78
|
+
"xl": 12
|
|
79
|
+
},
|
|
80
|
+
"powerThreshold": {
|
|
81
|
+
"type": "number",
|
|
82
|
+
"label": "Power-on threshold (W)",
|
|
83
|
+
"default": 10,
|
|
84
|
+
"min": 1,
|
|
85
|
+
"max": 500,
|
|
86
|
+
"help": "Power level from which the device is considered running. Recommended: 10W for all device types",
|
|
87
|
+
"xs": 12,
|
|
88
|
+
"sm": 12,
|
|
89
|
+
"md": 6,
|
|
90
|
+
"lg": 4,
|
|
91
|
+
"xl": 4
|
|
92
|
+
},
|
|
93
|
+
"offDelayMin": {
|
|
94
|
+
"type": "number",
|
|
95
|
+
"label": "Off delay (min)",
|
|
96
|
+
"default": 5,
|
|
97
|
+
"min": 1,
|
|
98
|
+
"max": 60,
|
|
99
|
+
"help": "How long power must stay below the threshold before the cycle ends. Recommended: washing machine 5 min · dryer 8 min · dishwasher 8 min · washer-dryer 10 min",
|
|
100
|
+
"xs": 12,
|
|
101
|
+
"sm": 12,
|
|
102
|
+
"md": 6,
|
|
103
|
+
"lg": 4,
|
|
104
|
+
"xl": 4
|
|
105
|
+
},
|
|
106
|
+
"startEnergyThreshold": {
|
|
107
|
+
"type": "number",
|
|
108
|
+
"label": "Start energy gate (Wh)",
|
|
109
|
+
"default": 2,
|
|
110
|
+
"min": 0,
|
|
111
|
+
"max": 50,
|
|
112
|
+
"help": "Minimum energy after start before the cycle is counted (prevents false starts). Recommended: washing machine 2 Wh · dryer 5 Wh · washer-dryer 5 Wh",
|
|
113
|
+
"xs": 12,
|
|
114
|
+
"sm": 12,
|
|
115
|
+
"md": 6,
|
|
116
|
+
"lg": 4,
|
|
117
|
+
"xl": 4
|
|
118
|
+
},
|
|
119
|
+
"_sep2": {
|
|
120
|
+
"type": "divider",
|
|
121
|
+
"xs": 12,
|
|
122
|
+
"sm": 12,
|
|
123
|
+
"md": 12,
|
|
124
|
+
"lg": 12,
|
|
125
|
+
"xl": 12
|
|
126
|
+
},
|
|
127
|
+
"_hdr2": {
|
|
128
|
+
"type": "staticText",
|
|
129
|
+
"text": "Program detection",
|
|
130
|
+
"style": {
|
|
131
|
+
"fontWeight": "bold",
|
|
132
|
+
"marginTop": "8px"
|
|
133
|
+
},
|
|
134
|
+
"xs": 12,
|
|
135
|
+
"sm": 12,
|
|
136
|
+
"md": 12,
|
|
137
|
+
"lg": 12,
|
|
138
|
+
"xl": 12
|
|
139
|
+
},
|
|
140
|
+
"durationTolerance": {
|
|
141
|
+
"type": "number",
|
|
142
|
+
"label": "Duration tolerance (0.05–0.5)",
|
|
143
|
+
"default": 0.2,
|
|
144
|
+
"min": 0.05,
|
|
145
|
+
"max": 0.5,
|
|
146
|
+
"step": 0.05,
|
|
147
|
+
"help": "How much the run time may deviate from the profile. 0.2 = ±20%",
|
|
148
|
+
"xs": 12,
|
|
149
|
+
"sm": 12,
|
|
150
|
+
"md": 6,
|
|
151
|
+
"lg": 4,
|
|
152
|
+
"xl": 4
|
|
153
|
+
},
|
|
154
|
+
"matchIntervalMin": {
|
|
155
|
+
"type": "number",
|
|
156
|
+
"label": "Matching interval (min)",
|
|
157
|
+
"default": 5,
|
|
158
|
+
"min": 1,
|
|
159
|
+
"max": 30,
|
|
160
|
+
"help": "How often the program is matched. Recommended: 5 min for all device types",
|
|
161
|
+
"xs": 12,
|
|
162
|
+
"sm": 12,
|
|
163
|
+
"md": 6,
|
|
164
|
+
"lg": 4,
|
|
165
|
+
"xl": 4
|
|
166
|
+
},
|
|
167
|
+
"matchPersist": {
|
|
168
|
+
"type": "number",
|
|
169
|
+
"label": "Match confirmations (count)",
|
|
170
|
+
"default": 3,
|
|
171
|
+
"min": 1,
|
|
172
|
+
"max": 5,
|
|
173
|
+
"help": "How many times in a row the same program must be detected. 3 = reliable · 2 = faster",
|
|
174
|
+
"xs": 12,
|
|
175
|
+
"sm": 12,
|
|
176
|
+
"md": 6,
|
|
177
|
+
"lg": 4,
|
|
178
|
+
"xl": 4
|
|
179
|
+
},
|
|
180
|
+
"autoConfirmThreshold": {
|
|
181
|
+
"type": "number",
|
|
182
|
+
"label": "Auto-confirm from confidence (%)",
|
|
183
|
+
"default": 85,
|
|
184
|
+
"min": 0,
|
|
185
|
+
"max": 100,
|
|
186
|
+
"help": "From this confidence level the cycle is confirmed automatically. 0 = disabled · 85 = recommended",
|
|
187
|
+
"xs": 12,
|
|
188
|
+
"sm": 12,
|
|
189
|
+
"md": 6,
|
|
190
|
+
"lg": 4,
|
|
191
|
+
"xl": 4
|
|
192
|
+
},
|
|
193
|
+
"instantConfirmThreshold": {
|
|
194
|
+
"type": "number",
|
|
195
|
+
"label": "Instant adoption from confidence (%)",
|
|
196
|
+
"default": 92,
|
|
197
|
+
"min": 50,
|
|
198
|
+
"max": 100,
|
|
199
|
+
"help": "During a running cycle: if the live preview repeatedly (2x) shows at least this confidence, the program is adopted immediately instead of waiting for the normal, slower detection.",
|
|
200
|
+
"xs": 12,
|
|
201
|
+
"sm": 12,
|
|
202
|
+
"md": 6,
|
|
203
|
+
"lg": 4,
|
|
204
|
+
"xl": 4
|
|
205
|
+
},
|
|
206
|
+
"matchThreshold": {
|
|
207
|
+
"type": "number",
|
|
208
|
+
"label": "Program detection threshold (%)",
|
|
209
|
+
"default": 55,
|
|
210
|
+
"min": 30,
|
|
211
|
+
"max": 95,
|
|
212
|
+
"help": "From this confidence a program counts as detected (and is shown). Below it, it stays 'detecting...'. 55 = recommended",
|
|
213
|
+
"xs": 12,
|
|
214
|
+
"sm": 12,
|
|
215
|
+
"md": 6,
|
|
216
|
+
"lg": 4,
|
|
217
|
+
"xl": 4
|
|
218
|
+
},
|
|
219
|
+
"notifyOnProbable": {
|
|
220
|
+
"type": "checkbox",
|
|
221
|
+
"label": "Notify also for probable program",
|
|
222
|
+
"default": false,
|
|
223
|
+
"help": "Send a Telegram update as soon as a program is detected - even if confidence is below the auto-confirm threshold",
|
|
224
|
+
"xs": 12,
|
|
225
|
+
"sm": 12,
|
|
226
|
+
"md": 6,
|
|
227
|
+
"lg": 4,
|
|
228
|
+
"xl": 4
|
|
229
|
+
},
|
|
230
|
+
"showProbableProgram": {
|
|
231
|
+
"type": "checkbox",
|
|
232
|
+
"label": "Show probable program before confirmation",
|
|
233
|
+
"default": false,
|
|
234
|
+
"help": "OFF (default): the program/programText data points show \"detecting...\" until a match is actually confirmed. ON: as soon as a live best-candidate guess reaches at least 40% confidence, it is shown immediately, prefixed with \"≈\" (e.g. \"≈ 30 Speed\") - updated continuously as matching confidence changes, not just once confirmed.",
|
|
235
|
+
"xs": 12,
|
|
236
|
+
"sm": 12,
|
|
237
|
+
"md": 6,
|
|
238
|
+
"lg": 4,
|
|
239
|
+
"xl": 4
|
|
240
|
+
},
|
|
241
|
+
"_sep3": {
|
|
242
|
+
"type": "divider",
|
|
243
|
+
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
244
|
+
"xs": 12,
|
|
245
|
+
"sm": 12,
|
|
246
|
+
"md": 12,
|
|
247
|
+
"lg": 12,
|
|
248
|
+
"xl": 12
|
|
249
|
+
},
|
|
250
|
+
"_hdr3": {
|
|
251
|
+
"type": "staticText",
|
|
252
|
+
"text": "Dryer settings",
|
|
253
|
+
"style": {
|
|
254
|
+
"fontWeight": "bold",
|
|
255
|
+
"marginTop": "8px"
|
|
256
|
+
},
|
|
257
|
+
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
258
|
+
"xs": 12,
|
|
259
|
+
"sm": 12,
|
|
260
|
+
"md": 12,
|
|
261
|
+
"lg": 12,
|
|
262
|
+
"xl": 12
|
|
263
|
+
},
|
|
264
|
+
"ignoreAntiKnitter": {
|
|
265
|
+
"type": "checkbox",
|
|
266
|
+
"label": "Ignore saved anti-crease pattern",
|
|
267
|
+
"default": true,
|
|
268
|
+
"help": "ON (default): the cycle ends immediately at the beep, and a saved anti-crease reference pattern (if any) is not used - simplest option, and required until a pattern has been recorded. OFF: once a reference pattern exists (see 'Programs' tab), the adapter uses it to avoid mistaking anti-crease power spikes for a new cycle after the beep. Turn this off once you have saved a pattern.",
|
|
269
|
+
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
270
|
+
"xs": 12,
|
|
271
|
+
"sm": 12,
|
|
272
|
+
"md": 6,
|
|
273
|
+
"lg": 4,
|
|
274
|
+
"xl": 4
|
|
275
|
+
},
|
|
276
|
+
"_sep4": {
|
|
277
|
+
"type": "divider",
|
|
278
|
+
"xs": 12,
|
|
279
|
+
"sm": 12,
|
|
280
|
+
"md": 12,
|
|
281
|
+
"lg": 12,
|
|
282
|
+
"xl": 12
|
|
283
|
+
},
|
|
284
|
+
"_hdr4": {
|
|
285
|
+
"type": "staticText",
|
|
286
|
+
"text": "Display language",
|
|
287
|
+
"style": {
|
|
288
|
+
"fontWeight": "bold",
|
|
289
|
+
"marginTop": "8px"
|
|
290
|
+
},
|
|
291
|
+
"xs": 12,
|
|
292
|
+
"sm": 12,
|
|
293
|
+
"md": 12,
|
|
294
|
+
"lg": 12,
|
|
295
|
+
"xl": 12
|
|
296
|
+
},
|
|
297
|
+
"displayLanguage": {
|
|
298
|
+
"type": "select",
|
|
299
|
+
"label": "Language for phaseText/stateText/programText",
|
|
300
|
+
"options": [
|
|
301
|
+
{
|
|
302
|
+
"label": "System default",
|
|
303
|
+
"value": ""
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
"label": "English",
|
|
307
|
+
"value": "en"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"label": "Deutsch",
|
|
311
|
+
"value": "de"
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
"label": "Русский",
|
|
315
|
+
"value": "ru"
|
|
316
|
+
},
|
|
317
|
+
{
|
|
318
|
+
"label": "Português",
|
|
319
|
+
"value": "pt"
|
|
320
|
+
},
|
|
321
|
+
{
|
|
322
|
+
"label": "Nederlands",
|
|
323
|
+
"value": "nl"
|
|
324
|
+
},
|
|
325
|
+
{
|
|
326
|
+
"label": "Français",
|
|
327
|
+
"value": "fr"
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
"label": "Italiano",
|
|
331
|
+
"value": "it"
|
|
332
|
+
},
|
|
333
|
+
{
|
|
334
|
+
"label": "Español",
|
|
335
|
+
"value": "es"
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
"label": "Polski",
|
|
339
|
+
"value": "pl"
|
|
340
|
+
},
|
|
341
|
+
{
|
|
342
|
+
"label": "Українська",
|
|
343
|
+
"value": "uk"
|
|
344
|
+
},
|
|
345
|
+
{
|
|
346
|
+
"label": "中文 (简体)",
|
|
347
|
+
"value": "zh-cn"
|
|
348
|
+
}
|
|
349
|
+
],
|
|
350
|
+
"default": "",
|
|
351
|
+
"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.",
|
|
352
|
+
"xs": 12,
|
|
353
|
+
"sm": 12,
|
|
354
|
+
"md": 6,
|
|
355
|
+
"lg": 4,
|
|
356
|
+
"xl": 4
|
|
357
|
+
},
|
|
358
|
+
"noEmoji": {
|
|
359
|
+
"type": "checkbox",
|
|
360
|
+
"label": "Text data points without emoji",
|
|
361
|
+
"default": false,
|
|
362
|
+
"help": "OFF (default): the phaseText/stateText data points include a leading/trailing emoji (e.g. \"🫧 Washing\", \"Running ⚙️\"). ON: the emoji is omitted - useful for dashboards, text-to-speech, or anywhere emoji don't render usefully.",
|
|
363
|
+
"xs": 12,
|
|
364
|
+
"sm": 12,
|
|
365
|
+
"md": 6,
|
|
366
|
+
"lg": 4,
|
|
367
|
+
"xl": 4
|
|
41
368
|
}
|
|
42
|
-
],
|
|
43
|
-
"xs": 12,
|
|
44
|
-
"sm": 12,
|
|
45
|
-
"md": 6,
|
|
46
|
-
"lg": 4,
|
|
47
|
-
"xl": 4
|
|
48
|
-
},
|
|
49
|
-
"powerId": {
|
|
50
|
-
"type": "objectId",
|
|
51
|
-
"label": "Power sensor (watt)",
|
|
52
|
-
"help": "ioBroker data point providing the current power draw in watts",
|
|
53
|
-
"xs": 12,
|
|
54
|
-
"sm": 12,
|
|
55
|
-
"md": 12,
|
|
56
|
-
"lg": 6,
|
|
57
|
-
"xl": 6
|
|
58
|
-
},
|
|
59
|
-
"_sep1": {
|
|
60
|
-
"type": "divider",
|
|
61
|
-
"xs": 12,
|
|
62
|
-
"sm": 12,
|
|
63
|
-
"md": 12,
|
|
64
|
-
"lg": 12,
|
|
65
|
-
"xl": 12
|
|
66
|
-
},
|
|
67
|
-
"_hdr1": {
|
|
68
|
-
"type": "staticText",
|
|
69
|
-
"text": "Detection thresholds",
|
|
70
|
-
"style": {
|
|
71
|
-
"fontWeight": "bold",
|
|
72
|
-
"marginTop": "8px"
|
|
73
|
-
},
|
|
74
|
-
"xs": 12,
|
|
75
|
-
"sm": 12,
|
|
76
|
-
"md": 12,
|
|
77
|
-
"lg": 12,
|
|
78
|
-
"xl": 12
|
|
79
|
-
},
|
|
80
|
-
"powerThreshold": {
|
|
81
|
-
"type": "number",
|
|
82
|
-
"label": "Power-on threshold (W)",
|
|
83
|
-
"default": 10,
|
|
84
|
-
"min": 1,
|
|
85
|
-
"max": 500,
|
|
86
|
-
"help": "Power level from which the device is considered running. Recommended: 10W for all device types",
|
|
87
|
-
"xs": 12,
|
|
88
|
-
"sm": 12,
|
|
89
|
-
"md": 6,
|
|
90
|
-
"lg": 4,
|
|
91
|
-
"xl": 4
|
|
92
|
-
},
|
|
93
|
-
"offDelayMin": {
|
|
94
|
-
"type": "number",
|
|
95
|
-
"label": "Off delay (min)",
|
|
96
|
-
"default": 5,
|
|
97
|
-
"min": 1,
|
|
98
|
-
"max": 60,
|
|
99
|
-
"help": "How long power must stay below the threshold before the cycle ends. Recommended: washing machine 5 min · dryer 8 min · dishwasher 8 min · washer-dryer 10 min",
|
|
100
|
-
"xs": 12,
|
|
101
|
-
"sm": 12,
|
|
102
|
-
"md": 6,
|
|
103
|
-
"lg": 4,
|
|
104
|
-
"xl": 4
|
|
105
|
-
},
|
|
106
|
-
"startEnergyThreshold": {
|
|
107
|
-
"type": "number",
|
|
108
|
-
"label": "Start energy gate (Wh)",
|
|
109
|
-
"default": 2,
|
|
110
|
-
"min": 0,
|
|
111
|
-
"max": 50,
|
|
112
|
-
"help": "Minimum energy after start before the cycle is counted (prevents false starts). Recommended: washing machine 2 Wh · dryer 5 Wh · washer-dryer 5 Wh",
|
|
113
|
-
"xs": 12,
|
|
114
|
-
"sm": 12,
|
|
115
|
-
"md": 6,
|
|
116
|
-
"lg": 4,
|
|
117
|
-
"xl": 4
|
|
118
|
-
},
|
|
119
|
-
"_sep2": {
|
|
120
|
-
"type": "divider",
|
|
121
|
-
"xs": 12,
|
|
122
|
-
"sm": 12,
|
|
123
|
-
"md": 12,
|
|
124
|
-
"lg": 12,
|
|
125
|
-
"xl": 12
|
|
126
|
-
},
|
|
127
|
-
"_hdr2": {
|
|
128
|
-
"type": "staticText",
|
|
129
|
-
"text": "Program detection",
|
|
130
|
-
"style": {
|
|
131
|
-
"fontWeight": "bold",
|
|
132
|
-
"marginTop": "8px"
|
|
133
|
-
},
|
|
134
|
-
"xs": 12,
|
|
135
|
-
"sm": 12,
|
|
136
|
-
"md": 12,
|
|
137
|
-
"lg": 12,
|
|
138
|
-
"xl": 12
|
|
139
|
-
},
|
|
140
|
-
"durationTolerance": {
|
|
141
|
-
"type": "number",
|
|
142
|
-
"label": "Duration tolerance (0.05–0.5)",
|
|
143
|
-
"default": 0.2,
|
|
144
|
-
"min": 0.05,
|
|
145
|
-
"max": 0.5,
|
|
146
|
-
"step": 0.05,
|
|
147
|
-
"help": "How much the run time may deviate from the profile. 0.2 = ±20%",
|
|
148
|
-
"xs": 12,
|
|
149
|
-
"sm": 12,
|
|
150
|
-
"md": 6,
|
|
151
|
-
"lg": 4,
|
|
152
|
-
"xl": 4
|
|
153
|
-
},
|
|
154
|
-
"matchIntervalMin": {
|
|
155
|
-
"type": "number",
|
|
156
|
-
"label": "Matching interval (min)",
|
|
157
|
-
"default": 5,
|
|
158
|
-
"min": 1,
|
|
159
|
-
"max": 30,
|
|
160
|
-
"help": "How often the program is matched. Recommended: 5 min for all device types",
|
|
161
|
-
"xs": 12,
|
|
162
|
-
"sm": 12,
|
|
163
|
-
"md": 6,
|
|
164
|
-
"lg": 4,
|
|
165
|
-
"xl": 4
|
|
166
|
-
},
|
|
167
|
-
"matchPersist": {
|
|
168
|
-
"type": "number",
|
|
169
|
-
"label": "Match confirmations (count)",
|
|
170
|
-
"default": 3,
|
|
171
|
-
"min": 1,
|
|
172
|
-
"max": 5,
|
|
173
|
-
"help": "How many times in a row the same program must be detected. 3 = reliable · 2 = faster",
|
|
174
|
-
"xs": 12,
|
|
175
|
-
"sm": 12,
|
|
176
|
-
"md": 6,
|
|
177
|
-
"lg": 4,
|
|
178
|
-
"xl": 4
|
|
179
|
-
},
|
|
180
|
-
"autoConfirmThreshold": {
|
|
181
|
-
"type": "number",
|
|
182
|
-
"label": "Auto-confirm from confidence (%)",
|
|
183
|
-
"default": 85,
|
|
184
|
-
"min": 0,
|
|
185
|
-
"max": 100,
|
|
186
|
-
"help": "From this confidence level the cycle is confirmed automatically. 0 = disabled · 85 = recommended",
|
|
187
|
-
"xs": 12,
|
|
188
|
-
"sm": 12,
|
|
189
|
-
"md": 6,
|
|
190
|
-
"lg": 4,
|
|
191
|
-
"xl": 4
|
|
192
|
-
},
|
|
193
|
-
"instantConfirmThreshold": {
|
|
194
|
-
"type": "number",
|
|
195
|
-
"label": "Instant adoption from confidence (%)",
|
|
196
|
-
"default": 92,
|
|
197
|
-
"min": 50,
|
|
198
|
-
"max": 100,
|
|
199
|
-
"help": "During a running cycle: if the live preview repeatedly (2x) shows at least this confidence, the program is adopted immediately instead of waiting for the normal, slower detection.",
|
|
200
|
-
"xs": 12,
|
|
201
|
-
"sm": 12,
|
|
202
|
-
"md": 6,
|
|
203
|
-
"lg": 4,
|
|
204
|
-
"xl": 4
|
|
205
|
-
},
|
|
206
|
-
"matchThreshold": {
|
|
207
|
-
"type": "number",
|
|
208
|
-
"label": "Program detection threshold (%)",
|
|
209
|
-
"default": 55,
|
|
210
|
-
"min": 30,
|
|
211
|
-
"max": 95,
|
|
212
|
-
"help": "From this confidence a program counts as detected (and is shown). Below it, it stays 'detecting...'. 55 = recommended",
|
|
213
|
-
"xs": 12,
|
|
214
|
-
"sm": 12,
|
|
215
|
-
"md": 6,
|
|
216
|
-
"lg": 4,
|
|
217
|
-
"xl": 4
|
|
218
|
-
},
|
|
219
|
-
"notifyOnProbable": {
|
|
220
|
-
"type": "checkbox",
|
|
221
|
-
"label": "Notify also for probable program",
|
|
222
|
-
"default": false,
|
|
223
|
-
"help": "Send a Telegram update as soon as a program is detected - even if confidence is below the auto-confirm threshold",
|
|
224
|
-
"xs": 12,
|
|
225
|
-
"sm": 12,
|
|
226
|
-
"md": 6,
|
|
227
|
-
"lg": 4,
|
|
228
|
-
"xl": 4
|
|
229
|
-
},
|
|
230
|
-
"_sep3": {
|
|
231
|
-
"type": "divider",
|
|
232
|
-
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
233
|
-
"xs": 12,
|
|
234
|
-
"sm": 12,
|
|
235
|
-
"md": 12,
|
|
236
|
-
"lg": 12,
|
|
237
|
-
"xl": 12
|
|
238
|
-
},
|
|
239
|
-
"_hdr3": {
|
|
240
|
-
"type": "staticText",
|
|
241
|
-
"text": "Dryer settings",
|
|
242
|
-
"style": {
|
|
243
|
-
"fontWeight": "bold",
|
|
244
|
-
"marginTop": "8px"
|
|
245
|
-
},
|
|
246
|
-
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
247
|
-
"xs": 12,
|
|
248
|
-
"sm": 12,
|
|
249
|
-
"md": 12,
|
|
250
|
-
"lg": 12,
|
|
251
|
-
"xl": 12
|
|
252
|
-
},
|
|
253
|
-
"ignoreAntiKnitter": {
|
|
254
|
-
"type": "checkbox",
|
|
255
|
-
"label": "Ignore saved anti-crease pattern",
|
|
256
|
-
"default": true,
|
|
257
|
-
"help": "ON (default): the cycle ends immediately at the beep, and a saved anti-crease reference pattern (if any) is not used - simplest option, and required until a pattern has been recorded. OFF: once a reference pattern exists (see 'Programs' tab), the adapter uses it to avoid mistaking anti-crease power spikes for a new cycle after the beep. Turn this off once you have saved a pattern.",
|
|
258
|
-
"hidden": "!data.deviceType || (data.deviceType !== \"dryer\")",
|
|
259
|
-
"xs": 12,
|
|
260
|
-
"sm": 12,
|
|
261
|
-
"md": 6,
|
|
262
|
-
"lg": 4,
|
|
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
|
|
310
369
|
}
|
|
311
|
-
}
|
|
312
370
|
}
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,21 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "laundrylens",
|
|
4
|
-
"version": "0.4.
|
|
4
|
+
"version": "0.4.33",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.4.33": {
|
|
7
|
+
"en": "Fix: a new cycle could show a leftover probable-program guess from the *previous*, already-finished cycle for several minutes (e.g. \"~30 Speed (76%)\" right at cycle start) - the internal best-candidate field wasn't cleared when a new cycle started. Fix: cycleProgress stayed stuck at its last value (often 100%) after a cycle finished instead of resetting to 0. Fix: an exception during cycle-finish post-processing (notably the post-hoc phase analysis for washing machines/dishwashers) could silently block the live status data points from updating. New option \"Show probable program before confirmation\": optionally surfaces the live best-candidate guess into the program/programText/confidence data points (prefixed with \"≈\"), not just in the admin tab's own preview. New option \"Text data points without emoji\": optionally omits the emoji from phaseText/stateText, for dashboards or text-to-speech.",
|
|
8
|
+
"de": "Fix: ein neuer Zyklus konnte minutenlang eine Restvermutung des *vorherigen*, bereits beendeten Zyklus anzeigen (z. B. \"~30 Speed (76%)\" direkt beim Start) - das interne Bestkandidat-Feld wurde beim Start eines neuen Zyklus nicht zurückgesetzt. Fix: cycleProgress blieb nach Zyklusende auf dem letzten Wert (oft 100%) hängen, statt auf 0 zurückgesetzt zu werden. Fix: eine Exception bei der Nachbearbeitung am Zyklusende (insbesondere die Post-hoc-Phasenanalyse für Waschmaschine/Spülmaschine) konnte die Aktualisierung der Live-Status-Datenpunkte stillschweigend blockieren. Neue Option \"Wahrscheinliches Programm vor Bestätigung anzeigen\": zeigt optional die Live-Bestkandidat-Vermutung in den Datenpunkten program/programText/confidence an (mit \"≈\"-Präfix), nicht nur in der Admin-Tab-eigenen Vorschau. Neue Option \"Text-Datenpunkte ohne Emoji\": lässt optional das Emoji in phaseText/stateText weg, für Dashboards oder Sprachausgabe.",
|
|
9
|
+
"ru": "Исправление: новый цикл мог несколько минут показывать остаточную вероятную программу от *предыдущего*, уже завершённого цикла (например, \"~30 Speed (76%)\" прямо в начале цикла) - внутреннее поле лучшего кандидата не сбрасывалось при старте нового цикла. Исправление: cycleProgress оставался на последнем значении (часто 100%) после завершения цикла вместо сброса на 0. Исправление: исключение при постобработке в конце цикла (в частности, постфактум-анализ фаз для стиральных/посудомоечных машин) могло незаметно блокировать обновление датапоинтов живого статуса. Новая опция \"Показывать вероятную программу до подтверждения\": опционально выводит текущую догадку лучшего кандидата в датапоинты program/programText/confidence (с префиксом \"≈\"), а не только в собственном превью вкладки администратора. Новая опция \"Текстовые датапоинты без эмодзи\": опционально убирает эмодзи из phaseText/stateText для дашбордов или синтеза речи.",
|
|
10
|
+
"pt": "Correção: um novo ciclo podia mostrar por vários minutos um palpite de programa provável restante do ciclo *anterior*, já finalizado (ex. \"~30 Speed (76%)\" logo no início do ciclo) - o campo interno de melhor candidato não era limpo ao iniciar um novo ciclo. Correção: cycleProgress ficava preso no último valor (frequentemente 100%) após o fim de um ciclo, em vez de voltar a 0. Correção: uma exceção durante o pós-processamento de fim de ciclo (notadamente a análise pós-fato de fases para máquinas de lavar/lava-louças) podia bloquear silenciosamente a atualização dos data points de status ao vivo. Nova opção \"Mostrar programa provável antes da confirmação\": exibe opcionalmente o palpite de melhor candidato ao vivo nos data points program/programText/confidence (prefixado com \"≈\"), não apenas na prévia da própria aba de administração. Nova opção \"Data points de texto sem emoji\": omite opcionalmente o emoji de phaseText/stateText, para painéis ou texto para fala.",
|
|
11
|
+
"nl": "Fix: een nieuwe cyclus kon enkele minuten lang een overgebleven waarschijnlijk-programma-gok van de *vorige*, al afgeronde cyclus tonen (bijv. \"~30 Speed (76%)\" meteen bij de start) - het interne beste-kandidaat-veld werd niet gewist bij de start van een nieuwe cyclus. Fix: cycleProgress bleef na afloop van een cyclus op de laatste waarde (vaak 100%) hangen in plaats van terug te gaan naar 0. Fix: een uitzondering tijdens de nabewerking aan het einde van een cyclus (met name de post-hoc fase-analyse voor wasmachines/vaatwassers) kon de update van de live-statusdatapoints stilzwijgend blokkeren. Nieuwe optie \"Waarschijnlijk programma tonen voor bevestiging\": toont optioneel de live beste-kandidaat-gok in de datapoints program/programText/confidence (met \"≈\"-voorvoegsel), niet alleen in de eigen preview van het admin-tabblad. Nieuwe optie \"Tekst-datapoints zonder emoji\": laat optioneel de emoji weg uit phaseText/stateText, voor dashboards of tekst-naar-spraak.",
|
|
12
|
+
"fr": "Correction : un nouveau cycle pouvait afficher pendant plusieurs minutes une estimation de programme probable restante du cycle *précédent*, déjà terminé (ex. \"~30 Speed (76%)\" dès le début du cycle) - le champ interne de meilleur candidat n'était pas réinitialisé au démarrage d'un nouveau cycle. Correction : cycleProgress restait bloqué sur sa dernière valeur (souvent 100 %) après la fin d'un cycle au lieu de revenir à 0. Correction : une exception pendant le post-traitement de fin de cycle (notamment l'analyse a posteriori des phases pour lave-linge/lave-vaisselle) pouvait bloquer silencieusement la mise à jour des points de données de statut en direct. Nouvelle option \"Afficher le programme probable avant confirmation\" : affiche en option l'estimation du meilleur candidat en direct dans les points de données program/programText/confidence (précédée de \"≈\"), pas seulement dans l'aperçu propre à l'onglet admin. Nouvelle option \"Points de données texte sans emoji\" : omet en option l'emoji de phaseText/stateText, pour les tableaux de bord ou la synthèse vocale.",
|
|
13
|
+
"it": "Correzione: un nuovo ciclo poteva mostrare per diversi minuti una stima di programma probabile residua del ciclo *precedente*, già terminato (es. \"~30 Speed (76%)\" subito all'inizio del ciclo) - il campo interno del miglior candidato non veniva azzerato all'avvio di un nuovo ciclo. Correzione: cycleProgress rimaneva bloccato sull'ultimo valore (spesso 100%) dopo la fine di un ciclo invece di tornare a 0. Correzione: un'eccezione durante la post-elaborazione di fine ciclo (in particolare l'analisi post-hoc delle fasi per lavatrici/lavastoviglie) poteva bloccare silenziosamente l'aggiornamento dei data point di stato live. Nuova opzione \"Mostra il programma probabile prima della conferma\": mostra opzionalmente la stima del miglior candidato live nei data point program/programText/confidence (con prefisso \"≈\"), non solo nell'anteprima della scheda admin. Nuova opzione \"Data point di testo senza emoji\": omette opzionalmente l'emoji da phaseText/stateText, per dashboard o sintesi vocale.",
|
|
14
|
+
"es": "Corrección: un nuevo ciclo podía mostrar durante varios minutos una suposición de programa probable sobrante del ciclo *anterior*, ya finalizado (p. ej. \"~30 Speed (76%)\" justo al inicio del ciclo) - el campo interno de mejor candidato no se reiniciaba al comenzar un nuevo ciclo. Corrección: cycleProgress se quedaba atascado en su último valor (a menudo 100%) tras finalizar un ciclo, en lugar de volver a 0. Corrección: una excepción durante el post-procesamiento al final del ciclo (en particular el análisis posterior de fases para lavadoras/lavavajillas) podía bloquear silenciosamente la actualización de los data points de estado en vivo. Nueva opción \"Mostrar programa probable antes de la confirmación\": muestra opcionalmente la suposición en vivo del mejor candidato en los data points program/programText/confidence (con prefijo \"≈\"), no solo en la vista previa propia de la pestaña de administración. Nueva opción \"Data points de texto sin emoji\": omite opcionalmente el emoji de phaseText/stateText, para paneles o texto a voz.",
|
|
15
|
+
"pl": "Poprawka: nowy cykl mógł przez kilka minut pokazywać pozostałość przypuszczenia programu z *poprzedniego*, już zakończonego cyklu (np. \"~30 Speed (76%)\" zaraz na starcie cyklu) - wewnętrzne pole najlepszego kandydata nie było czyszczone przy starcie nowego cyklu. Poprawka: cycleProgress pozostawał na ostatniej wartości (często 100%) po zakończeniu cyklu zamiast wrócić do 0. Poprawka: wyjątek podczas przetwarzania końcowego cyklu (zwłaszcza analiza post-hoc faz dla pralek/zmywarek) mógł po cichu blokować aktualizację datapointów statusu na żywo. Nowa opcja \"Pokaż prawdopodobny program przed potwierdzeniem\": opcjonalnie pokazuje bieżące przypuszczenie najlepszego kandydata w datapointach program/programText/confidence (z przedrostkiem \"≈\"), nie tylko we własnym podglądzie zakładki administratora. Nowa opcja \"Datapointy tekstowe bez emoji\": opcjonalnie pomija emoji w phaseText/stateText, na potrzeby dashboardów lub syntezy mowy.",
|
|
16
|
+
"uk": "Виправлення: новий цикл міг кілька хвилин показувати залишкове припущення ймовірної програми з *попереднього*, уже завершеного циклу (наприклад, \"~30 Speed (76%)\" одразу на старті циклу) - внутрішнє поле найкращого кандидата не скидалося під час старту нового циклу. Виправлення: cycleProgress залишався на останньому значенні (часто 100%) після завершення циклу замість скидання до 0. Виправлення: виняток під час постобробки наприкінці циклу (зокрема постфактум-аналіз фаз для пральних/посудомийних машин) міг непомітно блокувати оновлення датапоінтів живого статусу. Нова опція \"Показувати ймовірну програму до підтвердження\": опціонально виводить поточне припущення найкращого кандидата в датапоінти program/programText/confidence (з префіксом \"≈\"), а не лише у власному прев'ю вкладки адміністратора. Нова опція \"Текстові датапоінти без емодзі\": опціонально прибирає емодзі з phaseText/stateText для дашбордів або синтезу мовлення.",
|
|
17
|
+
"zh-cn": "修复:新周期可能会在最初几分钟内显示来自*上一个*已结束周期残留的可能程序猜测(例如周期刚开始就显示「~30 Speed (76%)」)——内部的最佳候选字段在新周期开始时未被清空。修复:周期结束后 cycleProgress 停留在最后的值(通常是 100%)而不会重置为 0。修复:周期结束后处理过程中的异常(尤其是洗衣机/洗碗机的事后阶段分析)可能会悄悄阻止实时状态数据点的更新。新增选项「确认前显示可能的程序」:可选择将实时最佳候选猜测显示到 program/programText/confidence 数据点中(带「≈」前缀),而不仅仅是管理标签页自身的预览。新增选项「文本数据点不含表情符号」:可选择在 phaseText/stateText 中省略表情符号,便于仪表盘或文本转语音使用。"
|
|
18
|
+
},
|
|
6
19
|
"0.4.32": {
|
|
7
20
|
"en": "Follow-up fix to 0.4.31: a restart alone did not clear an already-stale state data point (e.g. stuck on \"Running\"), even with 0.4.31 installed. onReady() only re-writes the state/stateText/running/program/phase data points when a restore branch fires (sensor currently active, or a saved cycle needs resuming/finishing) - with neither true, the common case, nothing touched them at all. Now every device's startup unconditionally re-syncs these data points with the manager's actual resolved state, so a restart always clears any stale value immediately, regardless of cause.",
|
|
8
21
|
"de": "Folgefix zu 0.4.31: ein reiner Neustart raeumte einen bereits haengengebliebenen Status-Datenpunkt (z.B. festgefahren auf \"Running\") nicht auf, selbst mit installiertem 0.4.31. onReady() schrieb die Datenpunkte state/stateText/running/program/phase bisher nur neu, wenn ein Restore-Zweig griff (Sensor aktuell aktiv, oder ein gespeicherter Zyklus muss fortgesetzt/beendet werden) - traf keins von beidem zu, der Normalfall, wurde nichts angefasst. Jetzt synchronisiert jeder Geraete-Start diese Datenpunkte bedingungslos mit dem tatsaechlichen, aufgeloesten Zustand des Managers, sodass ein Neustart jeden haengengebliebenen Wert sofort beseitigt, unabhaengig von der Ursache.",
|
|
@@ -80,19 +93,6 @@
|
|
|
80
93
|
"pl": "Poprawka: program poprawnie wykryty na początku cyklu mógł później, bliżej końca, ponownie pokazać \"wykrywanie...\", jeśli został potwierdzony z umiarkowaną, a nie bardzo wysoką pewnością (60-74%). Zabezpieczenie przed tym (blokowanie potwierdzonego programu, aby późniejsza seria niedopasowanych odczytów nie mogła go skasować) wcześniej aktywowało się tylko wtedy, gdy pojedynczy odczyt przypadkowo osiągnął też 75% pewności - teraz aktywuje się natychmiast przy każdym potwierdzeniu, niezależnie od potwierdzającej pewności.",
|
|
81
94
|
"uk": "Виправлення: програма, правильно визначена на початку циклу, могла пізніше знову показати «визначення...» ближче до кінця, якщо вона була підтверджена з помірною, а не дуже високою впевненістю (60-74%). Захист від цього (блокування підтвердженої програми, щоб пізніша серія невідповідних показань не могла її стерти) раніше активувався лише якщо якесь окреме показання випадково також досягало 75% впевненості - тепер він активується одразу під час кожного підтвердження, незалежно від підтверджувальної впевненості.",
|
|
82
95
|
"zh-cn": "修复:一个在周期早期被正确检测到的程序,如果是以中等置信度(60-74%)而非非常高的置信度被确认的,后期可能会重新显示为\"检测中...\"。此前,防止这种情况的保护机制(锁定已确认的程序,使后续一连串不匹配的读数无法将其清除)只有在某次单独读数恰好也达到 75% 置信度时才会启用——现在无论确认时的置信度如何,每次确认都会立即启用该保护。切换到确实不同程序的高置信度、持续性覆盖机制不受影响,仍然可用。"
|
|
83
|
-
},
|
|
84
|
-
"0.4.25": {
|
|
85
|
-
"en": "New: full per-phase duration learning model for remaining-time estimation. Each program now learns not just its overall duration, but how long each individual phase (heating, washing, spinning, dryer_drying, cooling, ...) typically takes, and in what order. Once enough data exists, remaining time is estimated as \"time left in the current phase, plus the typical duration of every phase that historically follows it\" - far more accurate near the end of a cycle than a single whole-cycle average, especially for a phase (like a dryer's main drying phase) whose position relative to total cycle length varies a lot between runs. Falls back to the existing 0.4.24 time/energy blend automatically until enough phase data has been learned (at least 3 confirmed cycles for the current phase).",
|
|
86
|
-
"de": "Neu: vollständiges Lernmodell für Phasendauern zur Restzeit-Schätzung. Jedes Programm lernt jetzt nicht nur seine Gesamtdauer, sondern auch, wie lange jede einzelne Phase (Aufheizen, Waschen, Schleudern, Trocknen, Abkühlen, ...) typischerweise dauert und in welcher Reihenfolge. Sobald genug Daten vorliegen, wird die Restzeit geschätzt als „verbleibende Zeit in der aktuellen Phase plus die typische Dauer aller Phasen, die historisch danach folgen\" - deutlich genauer gegen Ende eines Zyklus als ein einzelner Gesamtdurchschnitt, besonders bei einer Phase (wie der Haupttrocknungsphase beim Trockner), deren Position relativ zur Gesamtdauer stark zwischen den Läufen schwankt. Fällt automatisch auf die bestehende 0.4.24-Zeit/Energie-Mischung zurück, bis genug Phasendaten gelernt wurden (mindestens 3 bestätigte Zyklen für die aktuelle Phase).",
|
|
87
|
-
"ru": "Новое: полная модель обучения длительности фаз для оценки оставшегося времени. Теперь каждая программа изучает не только общую длительность, но и то, сколько времени обычно занимает каждая отдельная фаза и в каком порядке они идут. Как только накопится достаточно данных, оставшееся время оценивается как «время, оставшееся в текущей фазе, плюс типичная длительность всех фаз, которые исторически следуют за ней» - значительно точнее к концу цикла, чем единое общее среднее.",
|
|
88
|
-
"pt": "Novo: modelo completo de aprendizado de duração por fase para estimativa de tempo restante. Cada programa agora aprende não apenas sua duração total, mas também quanto tempo cada fase individual normalmente leva e em que ordem. Assim que houver dados suficientes, o tempo restante é estimado como \"tempo restante na fase atual, mais a duração típica de cada fase que historicamente a segue\" - muito mais preciso perto do fim de um ciclo do que uma única média de ciclo inteiro.",
|
|
89
|
-
"nl": "Nieuw: volledig leermodel voor fase-duur ten behoeve van de schatting van de resterende tijd. Elk programma leert nu niet alleen zijn totale duur, maar ook hoe lang elke afzonderlijke fase doorgaans duurt en in welke volgorde. Zodra er genoeg gegevens zijn, wordt de resterende tijd geschat als \"resterende tijd in de huidige fase, plus de typische duur van elke fase die er historisch op volgt\" - veel nauwkeuriger tegen het einde van een cyclus dan één gemiddelde over de hele cyclus.",
|
|
90
|
-
"fr": "Nouveau : modèle complet d'apprentissage de la durée par phase pour l'estimation du temps restant. Chaque programme apprend désormais non seulement sa durée globale, mais aussi la durée typique de chaque phase individuelle et leur ordre. Une fois suffisamment de données disponibles, le temps restant est estimé comme « le temps restant dans la phase actuelle, plus la durée typique de chaque phase qui la suit historiquement » - bien plus précis vers la fin d'un cycle qu'une simple moyenne globale.",
|
|
91
|
-
"it": "Novità: modello completo di apprendimento della durata per fase per la stima del tempo rimanente. Ogni programma ora impara non solo la sua durata complessiva, ma anche quanto dura tipicamente ogni singola fase e in quale ordine. Una volta disponibili dati sufficienti, il tempo rimanente viene stimato come \"tempo rimanente nella fase corrente, più la durata tipica di ogni fase che storicamente la segue\" - molto più preciso verso la fine di un ciclo rispetto a una singola media dell'intero ciclo.",
|
|
92
|
-
"es": "Novedad: modelo completo de aprendizaje de duración por fase para la estimación del tiempo restante. Cada programa ahora aprende no solo su duración total, sino también cuánto dura típicamente cada fase individual y en qué orden. Una vez que existen suficientes datos, el tiempo restante se estima como \"tiempo restante en la fase actual, más la duración típica de cada fase que históricamente la sigue\" - mucho más preciso cerca del final de un ciclo que un único promedio de todo el ciclo.",
|
|
93
|
-
"pl": "Nowość: pełny model uczenia się czasu trwania faz do szacowania pozostałego czasu. Każdy program uczy się teraz nie tylko całkowitego czasu trwania, ale także tego, jak długo zazwyczaj trwa każda pojedyncza faza i w jakiej kolejności. Gdy dostępnych jest wystarczająco dużo danych, pozostały czas jest szacowany jako \"czas pozostały w bieżącej fazie plus typowy czas trwania każdej fazy, która historycznie następuje po niej\" - znacznie dokładniejsze pod koniec cyklu niż pojedyncza średnia dla całego cyklu.",
|
|
94
|
-
"uk": "Новинка: повна модель навчання тривалості фаз для оцінки залишкового часу. Тепер кожна програма вивчає не лише загальну тривалість, а й те, скільки зазвичай триває кожна окрема фаза та в якому порядку. Щойно накопичиться достатньо даних, залишковий час оцінюється як «час, що залишився в поточній фазі, плюс типова тривалість кожної фази, яка історично йде після неї» - значно точніше наприкінці циклу, ніж єдине загальне середнє значення.",
|
|
95
|
-
"zh-cn": "新功能:用于剩余时间估算的完整分阶段时长学习模型。现在每个程序不仅学习总时长,还学习每个单独阶段(加热、洗涤、脱水、烘干、冷却等)通常需要多长时间,以及它们的先后顺序。一旦积累了足够的数据,剩余时间将被估算为\"当前阶段剩余时间,加上历史上在此之后各阶段的典型时长之和\"——这在周期接近结束时比单一的整体周期平均值准确得多,尤其适用于像烘干机主烘干阶段这种相对总时长位置波动很大的阶段。"
|
|
96
96
|
}
|
|
97
97
|
},
|
|
98
98
|
"titleLang": {
|
package/lib/displayLabels.js
CHANGED
|
@@ -33,16 +33,18 @@ const STATE_LABEL_KEYS = {
|
|
|
33
33
|
};
|
|
34
34
|
|
|
35
35
|
/**
|
|
36
|
-
* Returns the localized
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
36
|
+
* Returns the localized display text for a phase key (e.g. "washing" ->
|
|
37
|
+
* "🫧 Washing" / "🫧 Wäscht"). Falls back to the raw key itself if
|
|
38
|
+
* unknown, and to English if the given language has no translation for
|
|
39
|
+
* a known key.
|
|
40
40
|
*
|
|
41
41
|
* @param {string} phaseKey – neutral phase identifier (e.g. "washing")
|
|
42
42
|
* @param {string} lang – language code (e.g. "de"), defaults to "en"
|
|
43
|
-
* @
|
|
43
|
+
* @param {boolean} noEmoji – if true, omit the leading emoji (e.g. for
|
|
44
|
+
* dashboards/TTS where it doesn't render usefully)
|
|
45
|
+
* @returns {string} – e.g. "🫧 Washing", or "Washing" with noEmoji
|
|
44
46
|
*/
|
|
45
|
-
function getPhaseText(phaseKey, lang) {
|
|
47
|
+
function getPhaseText(phaseKey, lang, noEmoji) {
|
|
46
48
|
if (!phaseKey) {
|
|
47
49
|
return "";
|
|
48
50
|
}
|
|
@@ -51,9 +53,17 @@ function getPhaseText(phaseKey, lang) {
|
|
|
51
53
|
return phaseKey;
|
|
52
54
|
}
|
|
53
55
|
const text = entry[lang || "en"] || entry.en;
|
|
54
|
-
return `${entry.emoji} ${text}`;
|
|
56
|
+
return noEmoji ? text : `${entry.emoji} ${text}`;
|
|
55
57
|
}
|
|
56
58
|
|
|
59
|
+
// Matches one or more trailing emoji (each with an optional variation
|
|
60
|
+
// selector, e.g. the U+FE0F after ⚙) plus any surrounding whitespace, so
|
|
61
|
+
// it can be stripped from the end of an already-translated string like
|
|
62
|
+
// "Running ⚙️" / "Läuft ⚙️" - the emoji here is embedded in the
|
|
63
|
+
// translation itself, not a separate field the way phase labels have one.
|
|
64
|
+
const TRAILING_EMOJI_RE =
|
|
65
|
+
/\s*(?:[\u{1F300}-\u{1FFFF}\u{2600}-\u{27BF}]\u{FE0F}?\s*)+$/u;
|
|
66
|
+
|
|
57
67
|
/**
|
|
58
68
|
* Returns the localized display text for a device state
|
|
59
69
|
* (off/starting/running/paused/ending), using the same admin/i18n
|
|
@@ -62,14 +72,17 @@ function getPhaseText(phaseKey, lang) {
|
|
|
62
72
|
*
|
|
63
73
|
* @param {string} stateKey – off/starting/running/paused/ending
|
|
64
74
|
* @param {object} dict – translation dictionary (English key -> localized text)
|
|
65
|
-
* @
|
|
75
|
+
* @param {boolean} noEmoji – if true, strip any trailing emoji from the
|
|
76
|
+
* translated text (e.g. for dashboards/TTS)
|
|
77
|
+
* @returns {string} – e.g. "Running ⚙️", or "Running" with noEmoji
|
|
66
78
|
*/
|
|
67
|
-
function getStateText(stateKey, dict) {
|
|
79
|
+
function getStateText(stateKey, dict, noEmoji) {
|
|
68
80
|
const i18nKey = STATE_LABEL_KEYS[stateKey];
|
|
69
81
|
if (!i18nKey) {
|
|
70
82
|
return stateKey || "";
|
|
71
83
|
}
|
|
72
|
-
|
|
84
|
+
const text = (dict && dict[i18nKey]) || i18nKey;
|
|
85
|
+
return noEmoji ? text.replace(TRAILING_EMOJI_RE, "") : text;
|
|
73
86
|
}
|
|
74
87
|
|
|
75
88
|
/**
|
package/lib/washDataManager.js
CHANGED
|
@@ -503,6 +503,19 @@ class WashDataManager {
|
|
|
503
503
|
|
|
504
504
|
switch (newState) {
|
|
505
505
|
case STATES.STARTING:
|
|
506
|
+
// This is the single, authoritative reset point for every piece
|
|
507
|
+
// of per-cycle transient state: a new cycle must never show any
|
|
508
|
+
// leftover value from the one before it. Found live: _bestCandidate
|
|
509
|
+
// (the live "~Program (NN%)" preview the admin tab shows ahead of
|
|
510
|
+
// a confirmed match) was missing here, so a high-confidence match
|
|
511
|
+
// from the *previous*, already-finished cycle kept showing through
|
|
512
|
+
// for several minutes into the next cycle, before that cycle's own
|
|
513
|
+
// matching had run even once. The phase-tracking fields below are
|
|
514
|
+
// also normally cleared elsewhere (idle _buildStatus() calls /
|
|
515
|
+
// _onCycleFinished()'s _phaseHistory reset), but are reset here too
|
|
516
|
+
// as a deliberate second line of defense, so a cycle boundary is
|
|
517
|
+
// always a clean slate regardless of what runs (or fails to run,
|
|
518
|
+
// e.g. if _onCycleFinished() throws) in between.
|
|
506
519
|
this.cycleStartTime = eventData.timestamp;
|
|
507
520
|
this.currentProgram = null;
|
|
508
521
|
this.confidence = 0;
|
|
@@ -518,6 +531,12 @@ class WashDataManager {
|
|
|
518
531
|
this._lockedRemaining = null;
|
|
519
532
|
this._dryerDropTriggered = false;
|
|
520
533
|
this._dryerHighStart = null;
|
|
534
|
+
this._bestCandidate = null;
|
|
535
|
+
this._phaseHistory = [];
|
|
536
|
+
this._stablePhase = null;
|
|
537
|
+
this._phaseCandidate = null;
|
|
538
|
+
this._maxWatts = null;
|
|
539
|
+
this._phaseSM = null;
|
|
521
540
|
if (this._progressResetTimer) {
|
|
522
541
|
this.adapter.clearTimeout(this._progressResetTimer);
|
|
523
542
|
this._progressResetTimer = null;
|
|
@@ -535,7 +554,25 @@ class WashDataManager {
|
|
|
535
554
|
prevState === STATES.RUNNING ||
|
|
536
555
|
prevState === STATES.PAUSED
|
|
537
556
|
) {
|
|
538
|
-
this.
|
|
557
|
+
// Guarded: this.currentState is already correctly set to OFF
|
|
558
|
+
// above regardless of what happens here, but _onCycleFinished()
|
|
559
|
+
// runs a fair amount of post-processing (post-hoc phase
|
|
560
|
+
// analysis for washing machines/dishwashers, profile learning,
|
|
561
|
+
// file saves, sending the completion notification) that an
|
|
562
|
+
// uncaught exception anywhere inside would otherwise let
|
|
563
|
+
// escape this entire switch statement - silently skipping the
|
|
564
|
+
// onStateChange callback below and leaving the persisted
|
|
565
|
+
// state/stateText/running/program/phase data points stuck on
|
|
566
|
+
// their last value, exactly like the dryer anti-crease bug
|
|
567
|
+
// fixed in 0.4.31, but for any device type and any exception
|
|
568
|
+
// source rather than one specific missing callback call.
|
|
569
|
+
try {
|
|
570
|
+
this._onCycleFinished(eventData);
|
|
571
|
+
} catch (err) {
|
|
572
|
+
this.adapter.log.error(
|
|
573
|
+
`${this._name}: error finishing cycle: ${err.message}`,
|
|
574
|
+
);
|
|
575
|
+
}
|
|
539
576
|
}
|
|
540
577
|
break;
|
|
541
578
|
}
|
|
@@ -596,6 +633,22 @@ class WashDataManager {
|
|
|
596
633
|
this.config.durationTolerance || 0.2,
|
|
597
634
|
);
|
|
598
635
|
this._bestCandidate = best || null;
|
|
636
|
+
// Opt-in (config.showProbableProgram): surface the live best-candidate
|
|
637
|
+
// guess into the program/programText/confidence data points via the
|
|
638
|
+
// existing onProgramChange plumbing (same "≈ <name>" prefix the admin
|
|
639
|
+
// tab's own live preview already uses), instead of only updating the
|
|
640
|
+
// persisted data points once a program is actually confirmed. Only
|
|
641
|
+
// while nothing is confirmed yet - a locked/confirmed program always
|
|
642
|
+
// takes priority and is written by the branches further below.
|
|
643
|
+
if (
|
|
644
|
+
this.config.showProbableProgram &&
|
|
645
|
+
!this.currentProgram &&
|
|
646
|
+
best &&
|
|
647
|
+
best.confidence >= 0.4 &&
|
|
648
|
+
typeof this.callbacks.onProgramChange === "function"
|
|
649
|
+
) {
|
|
650
|
+
this.callbacks.onProgramChange(`≈ ${best.name}`, best.confidence);
|
|
651
|
+
}
|
|
599
652
|
const bestInfo = best
|
|
600
653
|
? `${best.name} (${Math.round((best.confidence || 0) * 100)}%)`
|
|
601
654
|
: "–";
|
|
@@ -780,7 +833,20 @@ class WashDataManager {
|
|
|
780
833
|
if (this._unmatchCount >= UNMATCH_PERSIST && this.currentProgram) {
|
|
781
834
|
this._revertToDetecting();
|
|
782
835
|
} else if (!this.currentProgram) {
|
|
783
|
-
|
|
836
|
+
// Don't immediately overwrite the probable-program guess fired
|
|
837
|
+
// above (same round, same this._bestCandidate check) with
|
|
838
|
+
// "detecting..." - "no confirmed match" and "no usable
|
|
839
|
+
// bestCandidate" are different things, and the whole point of
|
|
840
|
+
// showProbableProgram is to show the guess instead of
|
|
841
|
+
// "detecting..." whenever one exists.
|
|
842
|
+
const probableShown =
|
|
843
|
+
this.config.showProbableProgram &&
|
|
844
|
+
this._bestCandidate &&
|
|
845
|
+
this._bestCandidate.confidence >= 0.4;
|
|
846
|
+
if (
|
|
847
|
+
!probableShown &&
|
|
848
|
+
typeof this.callbacks.onProgramChange === "function"
|
|
849
|
+
) {
|
|
784
850
|
this.callbacks.onProgramChange("detecting...", 0);
|
|
785
851
|
}
|
|
786
852
|
}
|
package/main.js
CHANGED
|
@@ -203,6 +203,8 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
203
203
|
: 55,
|
|
204
204
|
notifyOnProbable: !!src.notifyOnProbable,
|
|
205
205
|
ignoreAntiKnitter: src.ignoreAntiKnitter !== false,
|
|
206
|
+
showProbableProgram: !!src.showProbableProgram,
|
|
207
|
+
noEmoji: !!src.noEmoji,
|
|
206
208
|
// "" (the default) means "use the ioBroker system language" -
|
|
207
209
|
// resolved once at startup, see onReady()'s _deviceLangCache setup.
|
|
208
210
|
displayLanguage: src.displayLanguage || "",
|
|
@@ -337,6 +339,7 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
337
339
|
instantConfirmThreshold: deviceCfg.instantConfirmThreshold ?? 92,
|
|
338
340
|
matchThreshold: deviceCfg.matchThreshold ?? 55,
|
|
339
341
|
ignoreAntiKnitter: deviceCfg.ignoreAntiKnitter,
|
|
342
|
+
showProbableProgram: deviceCfg.showProbableProgram,
|
|
340
343
|
},
|
|
341
344
|
{
|
|
342
345
|
onStateChange: (state, status) =>
|
|
@@ -525,7 +528,11 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
525
528
|
);
|
|
526
529
|
await this.setStateAsync(
|
|
527
530
|
`${deviceCfg.deviceId}.stateText`,
|
|
528
|
-
getStateText(
|
|
531
|
+
getStateText(
|
|
532
|
+
"running",
|
|
533
|
+
this._notifTranslations,
|
|
534
|
+
deviceCfg.noEmoji,
|
|
535
|
+
),
|
|
529
536
|
true,
|
|
530
537
|
);
|
|
531
538
|
await this.setStateAsync(
|
|
@@ -1392,11 +1399,15 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1392
1399
|
this._lastState[deviceId] = status.state;
|
|
1393
1400
|
const lang =
|
|
1394
1401
|
(this._deviceLangCache && this._deviceLangCache[deviceId]) || "en";
|
|
1402
|
+
const devCfgState = this._getDeviceConfig().find(
|
|
1403
|
+
(d) => d.deviceId === deviceId,
|
|
1404
|
+
);
|
|
1405
|
+
const noEmoji = devCfgState ? !!devCfgState.noEmoji : false;
|
|
1395
1406
|
|
|
1396
1407
|
this.setState(`${deviceId}.state`, status.state, true);
|
|
1397
1408
|
this.setState(
|
|
1398
1409
|
`${deviceId}.stateText`,
|
|
1399
|
-
getStateText(status.state, this._notifTranslations),
|
|
1410
|
+
getStateText(status.state, this._notifTranslations, noEmoji),
|
|
1400
1411
|
true,
|
|
1401
1412
|
);
|
|
1402
1413
|
this.setState(`${deviceId}.running`, status.running, true);
|
|
@@ -1432,7 +1443,7 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1432
1443
|
this.setState(`${deviceId}.phase`, phaseKey, true);
|
|
1433
1444
|
this.setState(
|
|
1434
1445
|
`${deviceId}.phaseText`,
|
|
1435
|
-
getPhaseText(phaseKey, lang),
|
|
1446
|
+
getPhaseText(phaseKey, lang, noEmoji),
|
|
1436
1447
|
true,
|
|
1437
1448
|
);
|
|
1438
1449
|
} else if (status.state === "off" || status.state === "ending") {
|
|
@@ -1488,7 +1499,13 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1488
1499
|
}
|
|
1489
1500
|
}
|
|
1490
1501
|
|
|
1491
|
-
_onTime(
|
|
1502
|
+
_onTime(
|
|
1503
|
+
deviceId,
|
|
1504
|
+
remainingSeconds,
|
|
1505
|
+
totalSeconds,
|
|
1506
|
+
progressPct,
|
|
1507
|
+
forceWrite = false,
|
|
1508
|
+
) {
|
|
1492
1509
|
// Phase bei jedem Tick schreiben
|
|
1493
1510
|
const mgrPh = this.managers[deviceId];
|
|
1494
1511
|
if (mgrPh) {
|
|
@@ -1530,8 +1547,15 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1530
1547
|
this.setState(`${deviceId}.elapsedTime`, elapsedSec, true);
|
|
1531
1548
|
}
|
|
1532
1549
|
this.setState(`${deviceId}.totalDuration`, totalSeconds ?? 0, true);
|
|
1533
|
-
// Only overwrite progress if > 0 (prevents a reset if the profile
|
|
1534
|
-
|
|
1550
|
+
// Only overwrite progress if > 0 (prevents a reset if the profile
|
|
1551
|
+
// briefly doesn't match) - UNLESS forceWrite is set, which is how
|
|
1552
|
+
// the deliberate reset-to-0 call at cycle end (_onCycleFinished())
|
|
1553
|
+
// bypasses this guard. Without forceWrite, that reset call's
|
|
1554
|
+
// progressPct=0 was silently swallowed by this same guard, which is
|
|
1555
|
+
// exactly why cycleProgress stayed stuck at its last value (often
|
|
1556
|
+
// 100%, or a stale percentage carried over from the previous cycle)
|
|
1557
|
+
// after a cycle had already finished - found live.
|
|
1558
|
+
if (progressPct > 0 || forceWrite) {
|
|
1535
1559
|
this.setState(`${deviceId}.cycleProgress`, progressPct, true);
|
|
1536
1560
|
}
|
|
1537
1561
|
}
|
|
@@ -1563,7 +1587,7 @@ class WashdataAdapter extends utils.Adapter {
|
|
|
1563
1587
|
Math.round((cycle.energyWh || 0) * 10) / 10,
|
|
1564
1588
|
true,
|
|
1565
1589
|
);
|
|
1566
|
-
this._onTime(deviceId, 0, 0, 0);
|
|
1590
|
+
this._onTime(deviceId, 0, 0, 0, true);
|
|
1567
1591
|
|
|
1568
1592
|
// Reset the program override
|
|
1569
1593
|
this.setState(`${deviceId}.programOverride`, "auto", true);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.laundrylens",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.33",
|
|
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",
|