iobroker.laundrylens 0.4.34 → 0.4.36

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -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.36 (2026-10-03)
136
+ - Fix: follow-up to 0.4.34's `needsFeedback` fix, reported live - deleting a cycle (e.g. an unconfirmed one someone just discards without confirming/correcting it first) could also leave `needsFeedback` stuck on `true` forever, since only `confirmCycle`/`correctCycle` had been covered. `clearAllData` and `importConfig` had the same gap (both can also replace `cycleHistory`/profiles wholesale). All three now also re-sync `needsFeedback`; `clearAllData`/`importConfig` additionally re-sync `availablePrograms`
137
+ - Covered by 3 new test cases extending `tests/test_programs_and_feedback_sync.js`
138
+
139
+ ### 0.4.35 (2026-10-03)
140
+ - Fix: reported live - `phaseText` could get stuck showing an earlier phase (e.g. "Aufheizen"/heating) while `phase` had correctly moved on (e.g. to "dryer_drying"). Root cause: `phase` was already written on every `_onTime()` tick (which fires frequently throughout a running cycle), but `phaseText` (the human-readable, localized, emoji-carrying label) was only ever written in `_onManagerState()` - which only runs on actual state transitions (off/starting/running/paused/ending), not on every phase change within a single long "running" period. Both data points now update together on every tick
141
+ - Covered by a new regression test (`tests/test_phase_text_tick_update.js`)
142
+
135
143
  ### 0.4.34 (2026-10-02)
136
144
  - Fix: reported live with screenshots - `availablePrograms` could show `[]` for a device that clearly had saved programs visible in the admin tab's own "Programme" list, while a second device correctly showed all of its programs. Root cause: the data point (and `programOverride`'s dropdown states) was only ever written from five specific admin-tab actions (create/delete/rename a program, "clear all data") - never at startup, and never after a program got auto-learned from a confirmed cycle. A device whose programs were all auto-learned, with none of those five actions performed since the last restart, kept showing an empty list indefinitely even though `profileStore` had loaded the real programs from disk
137
145
  - Fix: `needsFeedback` stayed `false` even with a cycle visibly pending confirmation in the admin tab's "Lernkontrolle" tab (complete with its own "1" badge). Root cause: the data point was declared in `io-package.json` but never actually written anywhere - the admin tab computes its own pending-cycle count entirely client-side (`updateFeedbackBadge()` in `tab_m.html`, counting cycles with `!confirmed`), so the data point itself never reflected it
package/io-package.json CHANGED
@@ -1,8 +1,34 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "laundrylens",
4
- "version": "0.4.34",
4
+ "version": "0.4.36",
5
5
  "news": {
6
+ "0.4.36": {
7
+ "en": "Follow-up to 0.4.34's needsFeedback fix: deleting a cycle (e.g. an unconfirmed one someone just discards without confirming/correcting it first), clearing all data, or importing a config could also leave needsFeedback stuck on true, since only confirmCycle/correctCycle had been covered. Now also re-synced after deleteCycle, clearAllData and importConfig (the latter two also re-sync availablePrograms, since they can add/replace profiles too).",
8
+ "de": "Folgefix zum needsFeedback-Fix aus 0.4.34: Das Löschen eines Zyklus (z. B. eines unbestätigten, den man einfach verwirft, ohne ihn vorher zu bestätigen/korrigieren), das Löschen aller Daten oder der Import einer Konfiguration konnten needsFeedback ebenfalls dauerhaft auf true belassen, da bisher nur confirmCycle/correctCycle abgedeckt waren. Wird jetzt auch nach deleteCycle, clearAllData und importConfig synchronisiert (die letzten beiden synchronisieren zusätzlich availablePrograms, da sie auch Programme hinzufügen/ersetzen können).",
9
+ "ru": "Дополнение к исправлению needsFeedback из 0.4.34: удаление цикла (например, неподтверждённого, который просто отклоняют без предварительного подтверждения/исправления), очистка всех данных или импорт конфигурации тоже могли оставить needsFeedback застрявшим на true, поскольку ранее были покрыты только confirmCycle/correctCycle. Теперь также синхронизируется после deleteCycle, clearAllData и importConfig (последние два дополнительно синхронизируют availablePrograms, так как они тоже могут добавлять/заменять программы).",
10
+ "pt": "Acompanhamento da correção do needsFeedback em 0.4.34: excluir um ciclo (ex. um não confirmado que alguém apenas descarta sem confirmar/corrigir antes), limpar todos os dados ou importar uma configuração também podiam deixar needsFeedback preso em true, já que só confirmCycle/correctCycle haviam sido cobertos. Agora também é ressincronizado após deleteCycle, clearAllData e importConfig (os dois últimos também ressincronizam availablePrograms, já que podem adicionar/substituir programas).",
11
+ "nl": "Vervolg op de needsFeedback-fix van 0.4.34: het verwijderen van een cyclus (bijv. een onbevestigde die iemand gewoon weggooit zonder deze eerst te bevestigen/corrigeren), alle data wissen, of een configuratie importeren konden needsFeedback ook vast laten zitten op true, aangezien alleen confirmCycle/correctCycle waren afgedekt. Wordt nu ook opnieuw gesynchroniseerd na deleteCycle, clearAllData en importConfig (de laatste twee synchroniseren ook availablePrograms, omdat ze ook programma's kunnen toevoegen/vervangen).",
12
+ "fr": "Suite de la correction needsFeedback de la 0.4.29 : supprimer un cycle (par ex. un cycle non confirmé que l'on écarte simplement sans le confirmer/corriger au préalable), effacer toutes les données ou importer une configuration pouvaient aussi laisser needsFeedback bloqué sur true, car seuls confirmCycle/correctCycle étaient couverts. Désormais resynchronisé aussi après deleteCycle, clearAllData et importConfig (ces deux derniers resynchronisent aussi availablePrograms, car ils peuvent également ajouter/remplacer des programmes).",
13
+ "it": "Seguito della correzione di needsFeedback della 0.4.34: eliminare un ciclo (es. uno non confermato che viene semplicemente scartato senza prima confermarlo/correggerlo), cancellare tutti i dati o importare una configurazione potevano anch'essi lasciare needsFeedback bloccato su true, poiché erano coperti solo confirmCycle/correctCycle. Ora risincronizzato anche dopo deleteCycle, clearAllData e importConfig (questi ultimi due risincronizzano anche availablePrograms, poiché possono anche aggiungere/sostituire programmi).",
14
+ "es": "Seguimiento de la corrección de needsFeedback de 0.4.34: eliminar un ciclo (p. ej. uno no confirmado que simplemente se descarta sin confirmarlo/corregirlo antes), borrar todos los datos o importar una configuración también podían dejar needsFeedback atascado en true, ya que solo confirmCycle/correctCycle estaban cubiertos. Ahora también se resincroniza tras deleteCycle, clearAllData e importConfig (estos dos últimos también resincronizan availablePrograms, ya que pueden añadir/sustituir programas).",
15
+ "pl": "Kontynuacja poprawki needsFeedback z 0.4.34: usunięcie cyklu (np. niepotwierdzonego, który ktoś po prostu odrzuca bez wcześniejszego potwierdzenia/poprawienia), wyczyszczenie wszystkich danych lub import konfiguracji mogły również pozostawić needsFeedback na stałe na true, ponieważ wcześniej objęto tylko confirmCycle/correctCycle. Teraz synchronizowane również po deleteCycle, clearAllData i importConfig (te dwa ostatnie synchronizują też availablePrograms, ponieważ mogą dodawać/zastępować programy).",
16
+ "uk": "Продовження виправлення needsFeedback з 0.4.34: видалення циклу (наприклад, непідтвердженого, який просто відхиляють без попереднього підтвердження/виправлення), очищення всіх даних або імпорт конфігурації також могли залишати needsFeedback застряглим на true, оскільки раніше були охоплені лише confirmCycle/correctCycle. Тепер також синхронізується після deleteCycle, clearAllData та importConfig (останні два також синхронізують availablePrograms, оскільки вони можуть додавати/замінювати програми).",
17
+ "zh-cn": "0.4.34 needsFeedback 修复的后续:删除某个周期(例如直接丢弃一个未确认的周期,而不先确认/更正它)、清除所有数据或导入配置,也可能导致 needsFeedback 一直卡在 true,因为此前只覆盖了 confirmCycle/correctCycle。现在 deleteCycle、clearAllData 和 importConfig 之后也会重新同步(后两者还会同步 availablePrograms,因为它们也可能添加/替换程序)。"
18
+ },
19
+ "0.4.35": {
20
+ "en": "Fix: phaseText could get stuck showing an earlier phase (e.g. \"Aufheizen\"/heating) while phase correctly moved on (e.g. to \"dryer_drying\") - phase was already updated on every tick, but phaseText (the human-readable, localized label) was only ever written at actual state transitions, not on every phase change within a single running cycle. Both now update together on every tick.",
21
+ "de": "Fix: phaseText konnte auf einer älteren Phase hängen bleiben (z. B. \"Aufheizen\"), während phase korrekt weiterlief (z. B. zu \"dryer_drying\") - phase wurde bereits bei jedem Tick aktualisiert, phaseText (das menschenlesbare, übersetzte Label) aber nur bei tatsächlichen Zustandsübergängen, nicht bei jedem Phasenwechsel innerhalb eines laufenden Zyklus. Beide werden jetzt gemeinsam bei jedem Tick aktualisiert.",
22
+ "ru": "Исправление: phaseText мог зависать на более ранней фазе (например, \"Aufheizen\"/нагрев), в то время как phase корректно переходил дальше (например, к \"dryer_drying\") - phase уже обновлялся при каждом тике, а phaseText (человекочитаемая, локализованная метка) записывался только при фактических переходах состояния, а не при каждой смене фазы внутри одного работающего цикла. Теперь оба обновляются вместе при каждом тике.",
23
+ "pt": "Correção: phaseText podia ficar preso mostrando uma fase anterior (ex. \"Aufheizen\"/aquecendo) enquanto phase avançava corretamente (ex. para \"dryer_drying\") - phase já era atualizado a cada tick, mas phaseText (o rótulo legível e localizado) só era escrito em transições de estado reais, não a cada mudança de fase dentro de um único ciclo em execução. Ambos agora são atualizados juntos a cada tick.",
24
+ "nl": "Fix: phaseText kon blijven hangen op een eerdere fase (bijv. \"Aufheizen\"/verwarmen) terwijl phase correct verderging (bijv. naar \"dryer_drying\") - phase werd al bij elke tick bijgewerkt, maar phaseText (het leesbare, gelokaliseerde label) werd alleen geschreven bij daadwerkelijke statusovergangen, niet bij elke fasewijziging binnen één lopende cyclus. Beide worden nu samen bij elke tick bijgewerkt.",
25
+ "fr": "Correction : phaseText pouvait rester bloqué sur une phase antérieure (ex. \"Aufheizen\"/chauffage) alors que phase progressait correctement (ex. vers \"dryer_drying\") - phase était déjà mis à jour à chaque tick, mais phaseText (l'étiquette lisible et localisée) n'était écrit qu'aux transitions d'état réelles, pas à chaque changement de phase au sein d'un même cycle en cours. Les deux sont désormais mis à jour ensemble à chaque tick.",
26
+ "it": "Correzione: phaseText poteva restare bloccato su una fase precedente (es. \"Aufheizen\"/riscaldamento) mentre phase avanzava correttamente (es. a \"dryer_drying\") - phase veniva già aggiornato a ogni tick, ma phaseText (l'etichetta leggibile e localizzata) veniva scritto solo alle transizioni di stato reali, non a ogni cambio di fase all'interno di un singolo ciclo in corso. Ora entrambi si aggiornano insieme a ogni tick.",
27
+ "es": "Corrección: phaseText podía quedarse atascado mostrando una fase anterior (p. ej. \"Aufheizen\"/calentando) mientras phase avanzaba correctamente (p. ej. a \"dryer_drying\") - phase ya se actualizaba en cada tick, pero phaseText (la etiqueta legible y localizada) solo se escribía en transiciones de estado reales, no en cada cambio de fase dentro de un mismo ciclo en ejecución. Ambos se actualizan ahora juntos en cada tick.",
28
+ "pl": "Poprawka: phaseText mógł utknąć na wcześniejszej fazie (np. \"Aufheizen\"/nagrzewanie), podczas gdy phase poprawnie szedł dalej (np. do \"dryer_drying\") - phase był już aktualizowany przy każdym tyknięciu, ale phaseText (czytelna, zlokalizowana etykieta) był zapisywany tylko przy faktycznych przejściach stanu, a nie przy każdej zmianie fazy w obrębie jednego trwającego cyklu. Oba są teraz aktualizowane razem przy każdym tyknięciu.",
29
+ "uk": "Виправлення: phaseText міг застрягати на попередній фазі (наприклад, \"Aufheizen\"/нагрівання), тоді як phase коректно просувався далі (наприклад, до \"dryer_drying\") - phase вже оновлювався при кожному тіку, а phaseText (зрозуміла, локалізована мітка) записувався лише при фактичних переходах стану, а не при кожній зміні фази в межах одного робочого циклу. Тепер обидва оновлюються разом при кожному тіку.",
30
+ "zh-cn": "修复:phaseText 可能会卡在较早的阶段(例如「Aufheizen」/加热),而 phase 却正确地推进(例如到「dryer_drying」)——phase 早已在每次心跳时更新,但 phaseText(人类可读的本地化标签)此前只在实际状态转换时才写入,而不是在单次运行周期内每次阶段变化时都写入。现在两者会在每次心跳时一起更新。"
31
+ },
6
32
  "0.4.34": {
7
33
  "en": "Fix: availablePrograms could show [] for a device that clearly had saved programs (visible in the admin tab's own Programme list) - it was only ever written from specific admin-tab actions (create/delete/rename a program, clear all data), never at startup or after a program got auto-learned from a confirmed cycle. Fix: needsFeedback stayed false even with a cycle visibly pending confirmation in the admin tab's Lernkontrolle tab - the data point was declared but never actually written anywhere; the admin tab computed its own pending-count entirely client-side. Both are now kept in sync at adapter startup, after every finished cycle, and (for needsFeedback) after a cycle is confirmed or corrected.",
8
34
  "de": "Fix: availablePrograms konnte [] anzeigen, obwohl ein Gerät eindeutig gespeicherte Programme hatte (sichtbar in der Admin-Tab-eigenen Programme-Liste) - der Datenpunkt wurde nur von bestimmten Admin-Tab-Aktionen geschrieben (Programm anlegen/löschen/umbenennen, alle Daten löschen), nie beim Start oder nachdem ein Programm aus einem bestätigten Zyklus automatisch gelernt wurde. Fix: needsFeedback blieb auf false, obwohl im Lernkontrolle-Tab des Admin-Tabs sichtbar ein Zyklus auf Bestätigung wartete - der Datenpunkt war deklariert, wurde aber nirgends tatsächlich beschrieben; der Admin-Tab berechnete seine eigene Anzahl offener Zyklen komplett clientseitig. Beide werden jetzt beim Adapterstart, nach jedem beendeten Zyklus und (bei needsFeedback) nach einer Bestätigung/Korrektur eines Zyklus synchron gehalten.",
@@ -67,32 +93,6 @@
67
93
  "pl": "Poprawka: elapsedTime deklaruje role \"value.interval\", ktora wymaga wartosci w sekundach, ale wszedzie byla obliczana i przechowywana w minutach (unit definicji stanu, jednorazowa migracja dla istniejacych instalacji, okresowa aktualizacja w main.js oraz zrzut na zywo odczytywany przez zakladke admina przez getStatus). Wszystkie cztery miejsca uzywaja teraz spojnie sekund, tak jak timeRemaining/totalDuration. Poprawka: usunieto ponownie devDependency sinon - jest juz dostarczana tranzytywnie przez @iobroker/testing, wiec bezposrednie jej wymienianie bylo zbedne (checker przegladu E0063). Poprawka: timery falszywego adaptera w kolejnym pliku testowym dostosowano do juz ustalonego w tym zestawie wzorca aliasow natywnych setTimeout/setInterval (checker przegladu E5004/E5005).",
68
94
  "uk": "Виправлення: elapsedTime оголошує роль \"value.interval\", яка вимагає значення в секундах, але воно всюди обчислювалося і зберігалося в хвилинах (unit у визначенні стану, одноразова міграція для наявних інсталяцій, періодичне оновлення в main.js та живий знімок, який вкладка адміністратора читає через getStatus). Тепер усі чотири місця узгоджено використовують секунди, як timeRemaining/totalDuration. Виправлення: devDependency sinon знову видалено - вона вже надається транзитивно через @iobroker/testing, тому пряме зазначення було зайвим (чекер перегляду E0063). Виправлення: таймери фіктивного адаптера ще одного тестового файлу переведено на вже усталений у цьому наборі шаблон нативних псевдонімів setTimeout/setInterval (чекер перегляду E5004/E5005).",
69
95
  "zh-cn": "修复:elapsedTime 声明的角色为「value.interval」,该角色要求数值以秒为单位,但此前在所有位置都以分钟计算和存储(状态定义的 unit、面向现有安装的一次性迁移、main.js 中的周期性更新,以及管理标签页通过 getStatus 读取的实时快照)。现在这四处已统一改为秒,与 timeRemaining/totalDuration 保持一致。修复:再次移除了 sinon 这一开发依赖——它已经通过 @iobroker/testing 间接提供,直接列出属于多余(审查检查器 E0063)。修复:另一个测试文件中伪造适配器的计时器已改为本测试套件中已确立的原生 setTimeout/setInterval 别名写法(审查检查器 E5004/E5005)。"
70
- },
71
- "0.4.28": {
72
- "en": "Dependency/repo-hygiene fixes reported by the ioBroker repository review: bumped @iobroker/testing to the current minimum (6.2.2). Replaced the GitHub noreply author email (not accepted by the review checker) with a real contact address in package.json, io-package.json and the README/LICENSE copyright lines. Reworked a test file's fake-adapter timers to use the same native-setTimeout/setInterval alias pattern already used everywhere else in the suite, so the checker's plain-timer-usage check no longer misidentifies it (the mock's job is exactly to forward to the real timer under the adapter.setTimeout name - behavior is unchanged).",
73
- "de": "Abhaengigkeits-/Repo-Hygiene-Fixes aus dem ioBroker-Repository-Review: @iobroker/testing auf die aktuell geforderte Mindestversion (6.2.2) angehoben. Die GitHub-Noreply-Autoren-E-Mail (vom Review-Checker nicht akzeptiert) in package.json, io-package.json sowie den Copyright-Zeilen von README/LICENSE durch eine echte Kontaktadresse ersetzt. Die Fake-Adapter-Timer einer Testdatei auf dasselbe native-setTimeout/setInterval-Alias-Muster umgestellt, das im Rest der Testsuite bereits ueberall verwendet wird, damit die Checker-Pruefung auf rohe Timer-Aufrufe die Datei nicht mehr faelschlich meldet (die Mock-Funktion leitet ohnehin nur unter dem Namen adapter.setTimeout an den echten Timer weiter - Verhalten unveraendert).",
74
- "ru": "Исправления зависимостей и гигиены репозитория по итогам ревью репозитория ioBroker: @iobroker/testing обновлён до текущего минимума (6.2.2). Адрес электронной почты автора GitHub noreply (не принимаемый чекером ревью) заменён на реальный контактный адрес в package.json, io-package.json и строках авторских прав README/LICENSE. Таймеры фиктивного адаптера в одном тестовом файле переведены на тот же шаблон нативных алиасов setTimeout/setInterval, который уже используется во всём остальном наборе тестов, чтобы проверка на использование простых таймеров больше не срабатывала ошибочно (мок и так лишь перенаправляет вызов под именем adapter.setTimeout к настоящему таймеру - поведение не изменилось).",
75
- "pt": "Correcoes de dependencias/higiene do repositorio apontadas pela revisao do repositorio ioBroker: @iobroker/testing atualizado para o minimo atual (6.2.2). O e-mail de autor noreply do GitHub (nao aceito pelo verificador de revisao) foi substituido por um endereco de contato real no package.json, io-package.json e nas linhas de copyright do README/LICENSE. Os temporizadores do adaptador simulado de um arquivo de teste foram ajustados para usar o mesmo padrao de alias nativo setTimeout/setInterval ja usado em todo o restante da suite, para que a verificacao de uso de temporizadores simples do checker deixe de identifica-lo incorretamente (o mock apenas encaminha para o temporizador real sob o nome adapter.setTimeout - o comportamento nao muda).",
76
- "nl": "Dependency-/repo-hygienefixes gemeld door de ioBroker-repository-review: @iobroker/testing bijgewerkt naar het huidige minimum (6.2.2). Het GitHub-noreply-auteurs-e-mailadres (niet geaccepteerd door de review-checker) vervangen door een echt contactadres in package.json, io-package.json en de copyright-regels van README/LICENSE. De nep-adaptertimers van een testbestand omgezet naar hetzelfde native-setTimeout/setInterval-aliaspatroon dat al overal elders in de testsuite wordt gebruikt, zodat de checkercontrole op kaal timergebruik het bestand niet langer ten onrechte meldt (de mock geeft toch al enkel door onder de naam adapter.setTimeout aan de echte timer - gedrag blijft ongewijzigd).",
77
- "fr": "Corrections de dependances/hygiene du depot signalees par la revue du depot ioBroker : @iobroker/testing mis a jour vers le minimum actuel (6.2.2). L'adresse e-mail d'auteur noreply de GitHub (non acceptee par le verificateur de revue) remplacee par une veritable adresse de contact dans package.json, io-package.json ainsi que les lignes de copyright du README/LICENSE. Les timers du faux adaptateur d'un fichier de test reorganises pour utiliser le meme modele d'alias natif setTimeout/setInterval deja utilise partout ailleurs dans la suite, afin que la verification des appels de timer bruts par le checker ne le signale plus a tort (le mock ne fait que relayer vers le vrai timer sous le nom adapter.setTimeout - le comportement reste inchange).",
78
- "it": "Correzioni di dipendenze/pulizia del repository segnalate dalla revisione del repository ioBroker: @iobroker/testing aggiornato al minimo attuale (6.2.2). L'indirizzo email dell'autore noreply di GitHub (non accettato dal checker di revisione) sostituito con un indirizzo di contatto reale in package.json, io-package.json e nelle righe di copyright di README/LICENSE. I timer del finto adattatore di un file di test riorganizzati per usare lo stesso schema di alias nativo setTimeout/setInterval gia usato ovunque nel resto della suite, cosi il controllo del checker sull'uso di timer semplici non lo segnala piu erroneamente (il mock si limita comunque a inoltrare al timer reale con il nome adapter.setTimeout - il comportamento resta invariato).",
79
- "es": "Correcciones de dependencias/higiene del repositorio senaladas por la revision del repositorio ioBroker: @iobroker/testing actualizado al minimo actual (6.2.2). La direccion de correo del autor noreply de GitHub (no aceptada por el verificador de revision) reemplazada por una direccion de contacto real en package.json, io-package.json y las lineas de copyright de README/LICENSE. Los temporizadores del adaptador simulado de un archivo de prueba reorganizados para usar el mismo patron de alias nativo setTimeout/setInterval ya usado en el resto de la suite, para que la verificacion del checker sobre el uso de temporizadores simples deje de detectarlo por error (el mock de todos modos solo reenvia al temporizador real bajo el nombre adapter.setTimeout - el comportamiento no cambia).",
80
- "pl": "Poprawki zaleznosci/higieny repozytorium zgloszone przez przeglad repozytorium ioBroker: zaktualizowano @iobroker/testing do aktualnego minimum (6.2.2). Adres e-mail autora typu noreply z GitHub (nieakceptowany przez checker przegladu) zastapiono prawdziwym adresem kontaktowym w package.json, io-package.json oraz w liniach praw autorskich README/LICENSE. Timery falszywego adaptera w jednym pliku testowym przebudowano tak, aby uzywaly tego samego wzorca aliasow natywnych setTimeout/setInterval, ktory jest juz uzywany wszedzie indziej w zestawie testow, dzieki czemu kontrola checkera dotyczaca golych wywolan timerow juz blednie tego nie zglasza (mock i tak jedynie przekazuje wywolanie do prawdziwego timera pod nazwa adapter.setTimeout - zachowanie sie nie zmienia).",
81
- "uk": "Виправлення залежностей і гігієни репозиторію, виявлені під час перегляду репозиторію ioBroker: @iobroker/testing оновлено до поточного мінімуму (6.2.2). Адресу електронної пошти автора GitHub noreply (яку не приймає чекер перегляду) замінено на справжню контактну адресу в package.json, io-package.json та рядках авторських прав README/LICENSE. Таймери фіктивного адаптера в одному тестовому файлі переведено на той самий шаблон нативних псевдонімів setTimeout/setInterval, який уже використовується всюди в решті набору тестів, щоб перевірка чекера на використання простих таймерів більше не позначала це помилково (мок і так лише передає виклик до справжнього таймера під назвою adapter.setTimeout - поведінка не змінюється).",
82
- "zh-cn": "根据 ioBroker 仓库审查报告修复的依赖/仓库卫生问题:将 @iobroker/testing 升级到当前要求的最低版本(6.2.2)。将审查检查器不接受的 GitHub noreply 作者邮箱,替换为 package.json、io-package.json 以及 README/LICENSE 版权行中的真实联系邮箱。将某测试文件中伪造适配器的计时器改为使用测试套件其余部分早已统一采用的原生 setTimeout/setInterval 别名写法,使检查器对「直接使用原生计时器」的检测不再误报该文件(该模拟函数本来就只是以 adapter.setTimeout 之名转发给真实计时器——行为未发生变化)。"
83
- },
84
- "0.4.27": {
85
- "en": "Fix: the notification-target dropdown showed a hardcoded German placeholder (\"Alle (broadcast)\") for every notification adapter except Telegram/email, regardless of the configured system language - now uses the existing translation everywhere. Fix: a Telegram user's display name (which any user messaging the configured bot can set arbitrarily) was written into the admin tab's dropdown options without HTML-escaping - now escaped before being inserted into the page.",
86
- "de": "Fix: Das Dropdown für das Benachrichtigungsziel zeigte bei jedem Benachrichtigungs-Adapter außer Telegram/E-Mail einen fest codierten deutschen Platzhalter (\"Alle (broadcast)\") an, unabhängig von der eingestellten Systemsprache - nutzt jetzt überall die vorhandene Übersetzung. Fix: Der Anzeigename eines Telegram-Nutzers (den jeder Nutzer, der dem konfigurierten Bot schreibt, beliebig setzen kann) wurde ohne HTML-Escaping in die Dropdown-Optionen des Admin-Tabs geschrieben - wird jetzt vor dem Einfügen escaped.",
87
- "ru": "Исправление: выпадающий список цели уведомления показывал жёстко заданный немецкий текст-заполнитель (\"Alle (broadcast)\") для любого адаптера уведомлений, кроме Telegram/email, независимо от настроенного системного языка - теперь везде используется существующий перевод. Исправление: отображаемое имя пользователя Telegram (которое может произвольно задать любой пользователь, написавший настроенному боту) записывалось в опции выпадающего списка вкладки администратора без экранирования HTML - теперь экранируется перед вставкой.",
88
- "pt": "Correção: o menu suspenso do destino da notificação mostrava um texto de espaço reservado alemão fixo (\"Alle (broadcast)\") para qualquer adaptador de notificação exceto Telegram/email, independentemente do idioma do sistema configurado - agora usa a tradução existente em todos os casos. Correção: o nome de exibição de um usuário do Telegram (que qualquer usuário que enviar mensagem ao bot configurado pode definir livremente) era escrito nas opções do menu suspenso da aba de administração sem escapar HTML - agora é escapado antes de ser inserido.",
89
- "nl": "Fix: de vervolgkeuzelijst voor het notificatiedoel toonde een hardgecodeerde Duitse plaatshoudertekst (\"Alle (broadcast)\") voor elke notificatie-adapter behalve Telegram/e-mail, ongeacht de ingestelde systeemtaal - gebruikt nu overal de bestaande vertaling. Fix: de weergavenaam van een Telegram-gebruiker (die elke gebruiker die de geconfigureerde bot een bericht stuurt willekeurig kan instellen) werd zonder HTML-escaping in de dropdown-opties van het admin-tabblad geschreven - wordt nu geëscaped voordat het wordt ingevoegd.",
90
- "fr": "Correction : le menu déroulant de la cible de notification affichait un texte de remplacement allemand codé en dur (\"Alle (broadcast)\") pour tout adaptateur de notification autre que Telegram/e-mail, quelle que soit la langue système configurée - utilise désormais la traduction existante partout. Correction : le nom d'affichage d'un utilisateur Telegram (que tout utilisateur écrivant au bot configuré peut définir arbitrairement) était écrit dans les options du menu déroulant de l'onglet admin sans échappement HTML - il est désormais échappé avant insertion.",
91
- "it": "Correzione: il menu a tendina della destinazione di notifica mostrava un testo segnaposto tedesco fisso (\"Alle (broadcast)\") per qualsiasi adattatore di notifica tranne Telegram/email, indipendentemente dalla lingua di sistema configurata - ora usa ovunque la traduzione esistente. Correzione: il nome visualizzato di un utente Telegram (che qualsiasi utente che scrive al bot configurato può impostare arbitrariamente) veniva scritto nelle opzioni del menu a tendina della scheda admin senza escaping HTML - ora viene sottoposto a escaping prima dell'inserimento.",
92
- "es": "Corrección: el menú desplegable del destino de notificación mostraba un texto de marcador de posición en alemán fijo (\"Alle (broadcast)\") para cualquier adaptador de notificación excepto Telegram/correo electrónico, sin importar el idioma del sistema configurado - ahora usa la traducción existente en todos los casos. Corrección: el nombre de visualización de un usuario de Telegram (que cualquier usuario que escriba al bot configurado puede establecer libremente) se escribía en las opciones del menú desplegable de la pestaña de administración sin escapar HTML - ahora se escapa antes de insertarse.",
93
- "pl": "Poprawka: rozwijana lista celu powiadomienia pokazywała na sztywno zakodowany niemiecki tekst zastępczy (\"Alle (broadcast)\") dla każdego adaptera powiadomień poza Telegram/e-mail, niezależnie od skonfigurowanego języka systemu - teraz wszędzie używa istniejącego tłumaczenia. Poprawka: wyświetlana nazwa użytkownika Telegram (którą może dowolnie ustawić każdy użytkownik piszący do skonfigurowanego bota) była zapisywana w opcjach rozwijanej listy zakładki administratora bez ucieczki HTML - teraz jest escapowana przed wstawieniem.",
94
- "uk": "Виправлення: спадний список цілі сповіщення показував жорстко закодований німецький текст-заповнювач (\"Alle (broadcast)\") для будь-якого адаптера сповіщень, крім Telegram/email, незалежно від налаштованої системної мови - тепер скрізь використовується наявний переклад. Виправлення: відображуване ім'я користувача Telegram (яке може довільно встановити будь-який користувач, що пише налаштованому боту) записувалося в опції спадного списку вкладки адміністратора без екранування HTML - тепер екранується перед вставкою.",
95
- "zh-cn": "修复:通知目标下拉菜单在除 Telegram/邮件之外的所有通知适配器中都显示硬编码的德语占位文本(\"Alle (broadcast)\"),与所配置的系统语言无关——现在统一使用现有的翻译。修复:Telegram 用户的显示名称(任何给已配置的机器人发消息的用户都可以任意设置)此前未经 HTML 转义就被写入管理标签页的下拉选项——现在会在插入前进行转义处理。"
96
96
  }
97
97
  },
98
98
  "titleLang": {
package/main.js CHANGED
@@ -1153,6 +1153,10 @@ class WashdataAdapter extends utils.Adapter {
1153
1153
  mgr.traceStore.deleteTrace(cycleId);
1154
1154
  await mgr._saveState();
1155
1155
  await mgr.traceStore.save();
1156
+ // Deleting a cycle - even an unconfirmed one, without ever
1157
+ // confirming/correcting it first - changes whether any cycle
1158
+ // still needs feedback, exactly like confirming one does.
1159
+ await this._updateNeedsFeedback(obj.message.deviceId, mgr);
1156
1160
  respond({ ok: true });
1157
1161
  break;
1158
1162
  }
@@ -1171,6 +1175,7 @@ class WashdataAdapter extends utils.Adapter {
1171
1175
  mgr._saveState(),
1172
1176
  ]);
1173
1177
  await this._updateOverrideStates(obj.message.deviceId, mgr);
1178
+ await this._updateNeedsFeedback(obj.message.deviceId, mgr);
1174
1179
  respond({ ok: true });
1175
1180
  break;
1176
1181
  }
@@ -1203,6 +1208,11 @@ class WashdataAdapter extends utils.Adapter {
1203
1208
  importedTraces = Object.keys(data.traces).length;
1204
1209
  }
1205
1210
 
1211
+ // Imported data can add profiles and/or replace cycleHistory
1212
+ // wholesale - same as any other action that changes either.
1213
+ await this._updateOverrideStates(obj.message.deviceId, mgr);
1214
+ await this._updateNeedsFeedback(obj.message.deviceId, mgr);
1215
+
1206
1216
  respond({
1207
1217
  ok: true,
1208
1218
  imported: data.profiles.length,
@@ -1540,10 +1550,29 @@ class WashdataAdapter extends utils.Adapter {
1540
1550
  if (mgrPh) {
1541
1551
  const st = mgrPh.getStatus();
1542
1552
  if (st && st.phase) {
1543
- const phaseText = st.phase
1553
+ const phaseKey = st.phase
1544
1554
  .replace(/^[\u{1F300}-\u{1FFFF}\u{2600}-\u{27BF}]\s*/u, "")
1545
1555
  .trim();
1546
- this.setState(`${deviceId}.phase`, phaseText, true);
1556
+ this.setState(`${deviceId}.phase`, phaseKey, true);
1557
+ // phaseText was only ever written in _onManagerState() - which
1558
+ // only fires on actual state transitions (off/starting/running/
1559
+ // paused/ending), not on every phase change within a single
1560
+ // long "running" period. phase (above) was already correctly
1561
+ // updated on every tick, which is exactly why it could show a
1562
+ // newer phase (e.g. "dryer_drying") while phaseText stayed
1563
+ // frozen on an older one from earlier in the same cycle (e.g.
1564
+ // "Aufheizen"/heating) - found live.
1565
+ const devCfgTime = this._getDeviceConfig().find(
1566
+ (d) => d.deviceId === deviceId,
1567
+ );
1568
+ const langTime =
1569
+ (this._deviceLangCache && this._deviceLangCache[deviceId]) || "en";
1570
+ const noEmojiTime = devCfgTime ? !!devCfgTime.noEmoji : false;
1571
+ this.setState(
1572
+ `${deviceId}.phaseText`,
1573
+ getPhaseText(phaseKey, langTime, noEmojiTime),
1574
+ true,
1575
+ );
1547
1576
  }
1548
1577
  }
1549
1578
  // Only check update messages every 60s AND only when a program is really recognized
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.laundrylens",
3
- "version": "0.4.34",
3
+ "version": "0.4.36",
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",