iobroker.poolcontrol 1.3.17 → 1.3.19
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 +15 -13
- package/io-package.json +27 -27
- package/lib/helpers/chemistryOrpHelper.js +34 -21
- package/lib/helpers/chemistryPhHelper.js +43 -26
- package/lib/helpers/chemistryTdsHelper.js +37 -24
- package/lib/helpers/runtimeHelper.js +51 -1
- package/lib/stateDefinitions/chemistryOrpStates.js +18 -18
- package/lib/stateDefinitions/chemistryPhStates.js +14 -14
- package/lib/stateDefinitions/chemistryTdsStates.js +16 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,6 +188,21 @@ New features are added regularly – please refer to the changelog.
|
|
|
188
188
|
---
|
|
189
189
|
|
|
190
190
|
## Changelog
|
|
191
|
+
### 1.3.19 (2026-05-13)
|
|
192
|
+
|
|
193
|
+
- Added runtime self-healing for missed pump start events
|
|
194
|
+
- Stabilized runtime and circulation calculations for delayed or filtered pump switch updates
|
|
195
|
+
- Fixed a rare synchronization issue where circulation counting could stop although pump live values were still available
|
|
196
|
+
- Improved internal runtime synchronization
|
|
197
|
+
|
|
198
|
+
### 1.3.18 (2026-05-11)
|
|
199
|
+
|
|
200
|
+
- Fixed incorrect date display in the pH, ORP and TDS areas.
|
|
201
|
+
- Time states with `value.time` are now stored as numeric timestamps instead of localized date strings.
|
|
202
|
+
- Improved compatibility with ioBroker/Admin date handling.
|
|
203
|
+
- Added backward-compatible handling for previously stored German date strings.
|
|
204
|
+
- Kept history JSON output unchanged with readable date strings for users and VIS displays.
|
|
205
|
+
|
|
191
206
|
### 1.3.17 (2026-05-11)
|
|
192
207
|
|
|
193
208
|
- Fixed release/upload issue from v1.3.16.
|
|
@@ -264,19 +279,6 @@ New features are added regularly – please refer to the changelog.
|
|
|
264
279
|
- Improved runtime timer cleanup and reset protection
|
|
265
280
|
- Improved speech logging for skipped and cooldown-limited announcements
|
|
266
281
|
|
|
267
|
-
### 1.3.14 (2026-05-08)
|
|
268
|
-
|
|
269
|
-
- Improved runtime handling and persistence
|
|
270
|
-
- Fixed season runtime calculation using the correct season state
|
|
271
|
-
- Unified live runtime calculations for total, today and season values
|
|
272
|
-
- Added numeric runtime second states for robust persistence and recovery
|
|
273
|
-
- Converted runtime timers to adapter-managed timers for improved ioBroker compatibility
|
|
274
|
-
|
|
275
|
-
### 1.3.13 (2026-05-08)
|
|
276
|
-
- (copilot) Adapter requires node.js >= 22 now
|
|
277
|
-
- Fixed invalid `common.installedFrom` entry in `io-package.json`
|
|
278
|
-
- Added German and English function overview documentation
|
|
279
|
-
|
|
280
282
|
## Support
|
|
281
283
|
- [ioBroker Forum](https://forum.iobroker.net/)
|
|
282
284
|
- [GitHub Issues](https://github.com/DasBo1975/ioBroker.poolcontrol/issues)
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,34 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "poolcontrol",
|
|
4
|
-
"version": "1.3.
|
|
4
|
+
"version": "1.3.19",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.3.19": {
|
|
7
|
+
"en": "Added runtime self-healing for missed pump start events. Stabilized runtime and circulation calculations for delayed or filtered pump switch updates. Fixed a rare synchronization issue where circulation counting could stop although pump live values were still available. Improved internal runtime synchronization.",
|
|
8
|
+
"de": "Runtime-Selbstheilung für verpasste Pumpenstart-Ereignisse ergänzt. Laufzeit- und Umwälzberechnung bei verzögerten oder gefilterten Pumpenschalter-Updates stabilisiert. Seltenen Synchronisationsfehler behoben, bei dem die Umwälzberechnung trotz aktiver Pumpen-Livewerte stehen bleiben konnte. Interne Runtime-Synchronisierung verbessert.",
|
|
9
|
+
"ru": "Добавлено самовосстановление во время выполнения для пропущенных событий запуска насоса. Стабилизированные расчеты времени работы и циркуляции для отложенных или отфильтрованных обновлений переключателей насосов. Исправлена редкая проблема синхронизации, из-за которой подсчет циркуляции мог остановиться, хотя текущие значения насоса все еще были доступны. Улучшена внутренняя синхронизация времени выполнения.",
|
|
10
|
+
"pt": "Adicionada autocorreção em tempo de execução para eventos de inicialização de bomba perdidos. Cálculos de tempo de execução e circulação estabilizados para atualizações de interruptores de bomba filtrados ou atrasados. Foi corrigido um raro problema de sincronização em que a contagem de circulação poderia parar embora os valores ativos da bomba ainda estivessem disponíveis. Sincronização de tempo de execução interna aprimorada.",
|
|
11
|
+
"nl": "Zelfherstel tijdens runtime toegevoegd voor gemiste pompstartgebeurtenissen. Gestabiliseerde looptijd- en circulatieberekeningen voor vertraagde of gefilterde updates van pompschakelaars. Er is een zeldzaam synchronisatieprobleem opgelost waarbij het tellen van de circulatie kon stoppen, hoewel de live-waarden van de pomp nog steeds beschikbaar waren. Verbeterde interne runtime-synchronisatie.",
|
|
12
|
+
"fr": "Ajout de l'auto-réparation du temps d'exécution pour les événements de démarrage de pompe manqués. Calculs de temps de fonctionnement et de circulation stabilisés pour les mises à jour retardées ou filtrées des interrupteurs de pompe. Correction d'un problème de synchronisation rare où le comptage de circulation pouvait s'arrêter même si les valeurs en direct de la pompe étaient toujours disponibles. Synchronisation d'exécution interne améliorée.",
|
|
13
|
+
"it": "Aggiunta l'autoriparazione del tempo di esecuzione per gli eventi di avvio della pompa mancati. Calcoli di autonomia e circolazione stabilizzati per aggiornamenti ritardati o filtrati dell'interruttore della pompa. Risolto un raro problema di sincronizzazione per cui il conteggio della circolazione poteva interrompersi anche se i valori in tempo reale della pompa erano ancora disponibili. Sincronizzazione runtime interna migliorata.",
|
|
14
|
+
"es": "Se agregó autorreparación en tiempo de ejecución para eventos de inicio de bomba perdidos. Cálculos de circulación y tiempo de funcionamiento estabilizados para actualizaciones retrasadas o filtradas del interruptor de la bomba. Se solucionó un problema de sincronización poco común por el cual el conteo de circulación podía detenerse aunque los valores activos de la bomba todavía estaban disponibles. Sincronización interna del tiempo de ejecución mejorada.",
|
|
15
|
+
"pl": "Dodano funkcję samonaprawy w czasie wykonywania w przypadku zdarzeń związanych z nieudanym uruchomieniem pompy. Stabilizowane obliczenia czasu pracy i cyrkulacji dla opóźnionych lub filtrowanych aktualizacji przełączników pomp. Naprawiono rzadki problem z synchronizacją, w wyniku którego zliczanie cyrkulacji mogło zostać zatrzymane, mimo że wartości bieżące pompy były nadal dostępne. Ulepszona wewnętrzna synchronizacja środowiska wykonawczego.",
|
|
16
|
+
"uk": "Додано самовідновлення під час виконання для пропущених подій запуску насоса. Стабілізований час роботи та обчислення циркуляції для відкладених або фільтрованих оновлень перемикача насоса. Виправлено рідкісну проблему синхронізації, через яку підрахунок циркуляції міг припинитися, хоча поточні значення насоса все ще були доступні. Покращена внутрішня синхронізація часу виконання.",
|
|
17
|
+
"zh-cn": "为错过的泵启动事件添加了运行时自我修复功能。延迟或过滤泵开关更新的稳定运行时间和循环计算。修复了一个罕见的同步问题,即尽管泵的实时值仍然可用,但循环计数可能会停止。改进了内部运行时同步。"
|
|
18
|
+
},
|
|
19
|
+
"1.3.18": {
|
|
20
|
+
"en": "Fixed incorrect date display for pH, ORP and TDS time states by storing value.time states as numeric timestamps.",
|
|
21
|
+
"de": "Falsche Datumsanzeige für pH-, ORP- und TDS-Zeitzustände behoben, indem value.time-Zustände als numerische Zeitstempel gespeichert wurden.",
|
|
22
|
+
"ru": "Исправлено неправильное отображение даты для состояний времени pH, ОВП и TDS за счет сохранения состояний value.time в виде числовых меток времени.",
|
|
23
|
+
"pt": "Corrigida a exibição incorreta de data para estados de tempo de pH, ORP e TDS, armazenando estados de valor.tempo como carimbos de data/hora numéricos.",
|
|
24
|
+
"nl": "Foutieve datumweergave voor pH-, ORP- en TDS-tijdstatussen opgelost door value.time-statussen op te slaan als numerieke tijdstempels.",
|
|
25
|
+
"fr": "Correction de l'affichage incorrect de la date pour les états temporels pH, ORP et TDS en stockant les états value.time sous forme d'horodatages numériques.",
|
|
26
|
+
"it": "Risolto il problema con la visualizzazione errata della data per gli stati temporali pH, ORP e TDS memorizzando gli stati value.time come timestamp numerici.",
|
|
27
|
+
"es": "Se corrigió la visualización de fecha incorrecta para los estados de tiempo de pH, ORP y TDS al almacenar los estados de valor.hora como marcas de tiempo numéricas.",
|
|
28
|
+
"pl": "Naprawiono nieprawidłowe wyświetlanie daty dla stanów czasowych pH, ORP i TDS poprzez przechowywanie stanów wartość.czas jako numeryczne znaczniki czasu.",
|
|
29
|
+
"uk": "Виправлено неправильне відображення дати для часових станів pH, ORP і TDS шляхом збереження станів value.time як числових позначок часу.",
|
|
30
|
+
"zh-cn": "通过将 value.time 状态存储为数字时间戳,修复了 pH、ORP 和 TDS 时间状态的不正确日期显示。"
|
|
31
|
+
},
|
|
6
32
|
"1.3.17": {
|
|
7
33
|
"en": "Fixed release packaging issue from v1.3.16. Improved speech system stability, stabilized runtime persistence, reduced repeated solar notifications, fixed circulation calculation in time mode and prepared new ORP/Redox chemistry analysis area.",
|
|
8
34
|
"de": "Release-/Uploadproblem aus v1.3.16 behoben. Sprachsystem verbessert, Runtime-Persistenz stabilisiert, wiederholte Solarbenachrichtigungen reduziert, Umwälzberechnung im Zeitmodus korrigiert und neuer ORP-/Redox-Chemiebereich vorbereitet.",
|
|
@@ -41,32 +67,6 @@
|
|
|
41
67
|
"pl": "Dodano obsługę schładzania źródła mowy, ulepszoną kontrolę mowy w czasie słonecznym/czasem, dodano trwałe drugie stany czasu wykonania, ulepszoną logikę przywracania w czasie wykonywania i przekonwertowano liczniki czasu działania na liczniki czasu adaptera ioBroker.",
|
|
42
68
|
"uk": "Додано обробку перезарядки джерела мовлення, покращено керування мовленням із сонячними променями/часом, додано постійні другі стани під час виконання, покращено логіку відновлення під час виконання та перетворено таймери часу виконання на таймери адаптера ioBroker.",
|
|
43
69
|
"zh-cn": "添加了语音源冷却处理、改进了太阳/时间语音控制、添加了持久运行时第二状态、改进了运行时恢复逻辑并将运行时计时器转换为 ioBroker 适配器计时器。"
|
|
44
|
-
},
|
|
45
|
-
"1.3.14": {
|
|
46
|
-
"en": "Improved runtime handling and persistence. Fixed season runtime calculation to use the correct season state, improved live runtime updates, added robust numeric runtime second states for safer persistence and recovery, and converted runtime timers to adapter-managed timers for better ioBroker compatibility.",
|
|
47
|
-
"de": "Laufzeit-Handling und Persistenz verbessert. Saisonlaufzeit nutzt jetzt den korrekten Saison-Status, Live-Laufzeiten wurden vereinheitlicht, robuste Sekunden-States fuer Laufzeiten zur sicheren Wiederherstellung hinzugefuegt und Runtime-Timer auf adapterverwaltete Timer fuer bessere ioBroker-Kompatibilitaet umgestellt.",
|
|
48
|
-
"ru": "Улучшена обработка и сохранение во время выполнения. Исправлен расчет времени выполнения сезона для использования правильного состояния сезона, улучшены обновления времени выполнения в режиме реального времени, добавлены надежные числовые значения второго состояния времени выполнения для более безопасного сохранения и восстановления, а также преобразованы таймеры времени выполнения в таймеры, управляемые адаптером, для лучшей совместимости с ioBroker.",
|
|
49
|
-
"pt": "Melhor manipulação e persistência do tempo de execução. Cálculo de tempo de execução de temporada corrigido para usar o estado de temporada correto, atualizações de tempo de execução ao vivo aprimoradas, segundos estados de tempo de execução numéricos robustos adicionados para persistência e recuperação mais seguras e temporizadores de tempo de execução convertidos em temporizadores gerenciados por adaptador para melhor compatibilidade com ioBroker.",
|
|
50
|
-
"nl": "Verbeterde runtime-afhandeling en persistentie. Vaste seizoensruntimeberekening om de juiste seizoensstatus te gebruiken, verbeterde live runtime-updates, robuuste numerieke runtime-secondestatussen toegevoegd voor veiliger persistentie en herstel, en geconverteerde runtime-timers naar door adapter beheerde timers voor betere ioBroker-compatibiliteit.",
|
|
51
|
-
"fr": "Gestion de l'exécution et persistance améliorées. Correction du calcul de la durée d'exécution de la saison pour utiliser l'état de saison correct, mises à jour d'exécution en direct améliorées, ajout de seconds états d'exécution numériques robustes pour une persistance et une récupération plus sûres, et conversion des minuteries d'exécution en minuteries gérées par l'adaptateur pour une meilleure compatibilité ioBroker.",
|
|
52
|
-
"it": "Gestione e persistenza del runtime migliorate. Risolto il problema con il calcolo del runtime della stagione per utilizzare lo stato della stagione corretto, aggiornamenti migliorati del runtime in tempo reale, aggiunti robusti secondi stati di runtime numerici per persistenza e ripristino più sicuri e timer di runtime convertiti in timer gestiti dall'adattatore per una migliore compatibilità con ioBroker.",
|
|
53
|
-
"es": "Manejo y persistencia del tiempo de ejecución mejorados. Se corrigió el cálculo del tiempo de ejecución de la temporada para usar el estado correcto de la temporada, se mejoraron las actualizaciones del tiempo de ejecución en vivo, se agregaron segundos estados de tiempo de ejecución numéricos sólidos para una persistencia y recuperación más seguras y se convirtieron los temporizadores de tiempo de ejecución en temporizadores administrados por adaptador para una mejor compatibilidad con ioBroker.",
|
|
54
|
-
"pl": "Poprawiona obsługa i trwałość środowiska wykonawczego. Naprawiono obliczanie czasu wykonania sezonu w celu wykorzystania prawidłowego stanu sezonu, ulepszono aktualizacje czasu działania na żywo, dodano solidne numeryczne drugie stany czasu działania dla bezpieczniejszej trwałości i odzyskiwania oraz przekonwertowano liczniki czasu działania na zegary zarządzane przez adapter dla lepszej kompatybilności z ioBroker.",
|
|
55
|
-
"uk": "Покращена обробка та збереження часу виконання. Виправлено розрахунок часу виконання сезону для використання правильного стану сезону, покращено поточні оновлення середовища виконання, додано надійні численні секунди часу виконання для безпечнішого збереження та відновлення, а також перетворено таймери часу виконання на таймери, керовані адаптером, для кращої сумісності з ioBroker.",
|
|
56
|
-
"zh-cn": "改进了运行时处理和持久性。修复了季节运行时计算以使用正确的季节状态,改进了实时运行时更新,添加了强大的数字运行时第二状态以实现更安全的持久性和恢复,并将运行时计时器转换为适配器管理的计时器以实现更好的 ioBroker 兼容性。"
|
|
57
|
-
},
|
|
58
|
-
"1.3.13": {
|
|
59
|
-
"en": "Added German and English function overview documentation. Fixed invalid common.installedFrom entry in io-package.json.",
|
|
60
|
-
"de": "Deutsche und englische Funktionsübersicht ergänzt. Ungültigen common.installedFrom-Eintrag in der io-package.json korrigiert.",
|
|
61
|
-
"ru": "Добавлена обзорная документация по функциям на немецком и английском языках. Исправлена неверная запись common.installedFrom в io-package.json.",
|
|
62
|
-
"pt": "Adicionada documentação de visão geral das funções em alemão e inglês. Corrigida entrada common.installedFrom inválida em io-package.json.",
|
|
63
|
-
"nl": "Duitse en Engelse functieoverzichtdocumentatie toegevoegd. Ongeldige common.installedFrom-vermelding in io-package.json opgelost.",
|
|
64
|
-
"fr": "Ajout d'une documentation de présentation des fonctions en allemand et en anglais. Correction de l'entrée common.installedFrom invalide dans io-package.json.",
|
|
65
|
-
"it": "Aggiunta la documentazione panoramica delle funzioni in tedesco e inglese. Risolto il problema con la voce common.installedFrom non valida in io-package.json.",
|
|
66
|
-
"es": "Se agregó documentación de descripción general de funciones en alemán e inglés. Se corrigió la entrada common.installedFrom no válida en io-package.json.",
|
|
67
|
-
"pl": "Dodano dokumentację przeglądu funkcji w języku niemieckim i angielskim. Naprawiono nieprawidłowy wpis common.installedFrom w io-package.json.",
|
|
68
|
-
"uk": "Додано документацію щодо огляду функцій німецькою та англійською мовами. Виправлено недійсний запис common.installedFrom у io-package.json.",
|
|
69
|
-
"zh-cn": "添加了德语和英语功能概述文档。修复了 io-package.json 中无效的 common.installedFrom 条目。"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"titleLang": {
|
|
@@ -238,7 +238,7 @@ const chemistryOrpHelper = {
|
|
|
238
238
|
const now = new Date();
|
|
239
239
|
const value = Number(rawValue);
|
|
240
240
|
|
|
241
|
-
await this.
|
|
241
|
+
await this._setNumber('chemistry.orp.input.last_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
242
242
|
|
|
243
243
|
if (source === 'manual') {
|
|
244
244
|
await this._setBool('chemistry.orp.input.source_valid', true);
|
|
@@ -277,7 +277,7 @@ const chemistryOrpHelper = {
|
|
|
277
277
|
recommendation: measurementAllowed.recommendation,
|
|
278
278
|
});
|
|
279
279
|
await this._setString('chemistry.orp.debug.last_reason', reason || measurementAllowed.reason);
|
|
280
|
-
await this.
|
|
280
|
+
await this._setNumber('chemistry.orp.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
281
281
|
return;
|
|
282
282
|
}
|
|
283
283
|
|
|
@@ -296,7 +296,7 @@ const chemistryOrpHelper = {
|
|
|
296
296
|
await this._writeOutputs(value, phReference, trend, evaluation);
|
|
297
297
|
|
|
298
298
|
await this._setString('chemistry.orp.debug.last_reason', reason || 'value_processed');
|
|
299
|
-
await this.
|
|
299
|
+
await this._setNumber('chemistry.orp.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
300
300
|
},
|
|
301
301
|
|
|
302
302
|
async _checkMeasurementAllowed(now) {
|
|
@@ -385,22 +385,18 @@ const chemistryOrpHelper = {
|
|
|
385
385
|
|
|
386
386
|
async _updateLastValues(value, now) {
|
|
387
387
|
const lastValid = await this._readNumberOrNull('chemistry.orp.input.last_valid_value');
|
|
388
|
-
const lastValidAt = await this.
|
|
388
|
+
const lastValidAt = await this._readTimestampOrNull('chemistry.orp.input.last_valid_value_at'); // FIX: accept numeric ms timestamps and legacy German strings.
|
|
389
389
|
|
|
390
390
|
if (lastValid !== null && lastValidAt) {
|
|
391
391
|
await this._setNumber('chemistry.orp.input.previous_value', lastValid);
|
|
392
|
-
await this.
|
|
392
|
+
await this._setNumber('chemistry.orp.input.previous_value_at', lastValidAt); // FIX: pass stored timestamp through unchanged.
|
|
393
393
|
|
|
394
|
-
const
|
|
395
|
-
|
|
396
|
-
if (previousDate) {
|
|
397
|
-
const minutes = Math.max(0, Math.round((now.getTime() - previousDate.getTime()) / 60000));
|
|
398
|
-
await this._setNumber('chemistry.orp.input.minutes_since_previous_value', minutes);
|
|
399
|
-
}
|
|
394
|
+
const minutes = Math.max(0, Math.round((now.getTime() - lastValidAt) / 60000)); // FIX: calculate from numeric ms timestamp.
|
|
395
|
+
await this._setNumber('chemistry.orp.input.minutes_since_previous_value', minutes);
|
|
400
396
|
}
|
|
401
397
|
|
|
402
398
|
await this._setNumber('chemistry.orp.input.last_valid_value', value);
|
|
403
|
-
await this.
|
|
399
|
+
await this._setNumber('chemistry.orp.input.last_valid_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
404
400
|
},
|
|
405
401
|
|
|
406
402
|
async _updateHistory(value, now, forceSample) {
|
|
@@ -430,13 +426,15 @@ const chemistryOrpHelper = {
|
|
|
430
426
|
await this._setNumber('chemistry.orp.history.samples_count', samples.length);
|
|
431
427
|
|
|
432
428
|
if (samples.length) {
|
|
433
|
-
await this.
|
|
429
|
+
await this._setNumber(
|
|
430
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
434
431
|
'chemistry.orp.history.oldest_sample_at',
|
|
435
|
-
|
|
432
|
+
Number(samples[0].ts),
|
|
436
433
|
);
|
|
437
|
-
await this.
|
|
434
|
+
await this._setNumber(
|
|
435
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
438
436
|
'chemistry.orp.history.newest_sample_at',
|
|
439
|
-
|
|
437
|
+
Number(samples[samples.length - 1].ts),
|
|
440
438
|
);
|
|
441
439
|
}
|
|
442
440
|
|
|
@@ -544,15 +542,15 @@ const chemistryOrpHelper = {
|
|
|
544
542
|
|
|
545
543
|
async _writeTrend(trend) {
|
|
546
544
|
await this._setNumber('chemistry.orp.trend.reference_24h_value', trend.ref24h ? trend.ref24h.value : 0);
|
|
547
|
-
await this.
|
|
545
|
+
await this._setNumber('chemistry.orp.trend.reference_24h_at', trend.ref24h ? trend.ref24h.ts : 0); // FIX
|
|
548
546
|
await this._setNumber('chemistry.orp.trend.delta_24h', trend.ref24h ? trend.delta24h : 0);
|
|
549
547
|
|
|
550
548
|
await this._setNumber('chemistry.orp.trend.reference_7d_value', trend.ref7d ? trend.ref7d.value : 0);
|
|
551
|
-
await this.
|
|
549
|
+
await this._setNumber('chemistry.orp.trend.reference_7d_at', trend.ref7d ? trend.ref7d.ts : 0); // FIX
|
|
552
550
|
await this._setNumber('chemistry.orp.trend.delta_7d', trend.ref7d ? trend.delta7d : 0);
|
|
553
551
|
|
|
554
552
|
await this._setNumber('chemistry.orp.trend.reference_30d_value', trend.ref30d ? trend.ref30d.value : 0);
|
|
555
|
-
await this.
|
|
553
|
+
await this._setNumber('chemistry.orp.trend.reference_30d_at', trend.ref30d ? trend.ref30d.ts : 0); // FIX
|
|
556
554
|
await this._setNumber('chemistry.orp.trend.delta_30d', trend.ref30d ? trend.delta30d : 0);
|
|
557
555
|
|
|
558
556
|
await this._setString('chemistry.orp.trend.direction', trend.direction);
|
|
@@ -692,7 +690,8 @@ const chemistryOrpHelper = {
|
|
|
692
690
|
await this._setString('chemistry.orp.evaluation.recommendation', I18n.translate('ORP evaluation is disabled.'));
|
|
693
691
|
await this._setBool('chemistry.orp.evaluation.action_required', false);
|
|
694
692
|
await this._setString('chemistry.orp.debug.last_reason', reason);
|
|
695
|
-
|
|
693
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
694
|
+
await this._setNumber('chemistry.orp.debug.last_update', now.getTime()); // FIX
|
|
696
695
|
},
|
|
697
696
|
|
|
698
697
|
async _writeInvalid(recommendation, reason) {
|
|
@@ -705,7 +704,8 @@ const chemistryOrpHelper = {
|
|
|
705
704
|
await this._setString('chemistry.orp.evaluation.recommendation', recommendation);
|
|
706
705
|
await this._setBool('chemistry.orp.evaluation.action_required', false);
|
|
707
706
|
await this._setString('chemistry.orp.debug.last_reason', reason);
|
|
708
|
-
|
|
707
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
708
|
+
await this._setNumber('chemistry.orp.debug.last_update', now.getTime()); // FIX
|
|
709
709
|
},
|
|
710
710
|
|
|
711
711
|
async _readString(id) {
|
|
@@ -725,6 +725,19 @@ const chemistryOrpHelper = {
|
|
|
725
725
|
return Number.isFinite(value) ? value : null;
|
|
726
726
|
},
|
|
727
727
|
|
|
728
|
+
async _readTimestampOrNull(id) {
|
|
729
|
+
const state = await this.adapter.getStateAsync(id);
|
|
730
|
+
const value = state?.val;
|
|
731
|
+
const numberValue = Number(value);
|
|
732
|
+
|
|
733
|
+
if (Number.isFinite(numberValue) && numberValue > 0) {
|
|
734
|
+
return numberValue; // FIX: numeric value.time timestamps are used directly.
|
|
735
|
+
}
|
|
736
|
+
|
|
737
|
+
const legacyDate = this._parseGermanDateTime(value); // FIX: keep backward compatibility for old string values.
|
|
738
|
+
return legacyDate ? legacyDate.getTime() : null;
|
|
739
|
+
},
|
|
740
|
+
|
|
728
741
|
async _readBoolean(id) {
|
|
729
742
|
const state = await this.adapter.getStateAsync(id);
|
|
730
743
|
return !!state?.val;
|
|
@@ -215,7 +215,8 @@ const chemistryPhHelper = {
|
|
|
215
215
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
216
216
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'disabled');
|
|
217
217
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'disabled');
|
|
218
|
-
|
|
218
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
219
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
219
220
|
return;
|
|
220
221
|
}
|
|
221
222
|
|
|
@@ -234,7 +235,8 @@ const chemistryPhHelper = {
|
|
|
234
235
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
235
236
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'source_disabled');
|
|
236
237
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'source_disabled');
|
|
237
|
-
|
|
238
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
239
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
238
240
|
return;
|
|
239
241
|
}
|
|
240
242
|
|
|
@@ -263,7 +265,8 @@ const chemistryPhHelper = {
|
|
|
263
265
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
264
266
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'missing_source_state');
|
|
265
267
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'missing_source_state');
|
|
266
|
-
|
|
268
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
269
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
267
270
|
return;
|
|
268
271
|
}
|
|
269
272
|
|
|
@@ -287,7 +290,8 @@ const chemistryPhHelper = {
|
|
|
287
290
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
288
291
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'source_read_error');
|
|
289
292
|
await this._setString('chemistry.ph.debug.last_reason', `source_read_error: ${err.message}`);
|
|
290
|
-
|
|
293
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
294
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
291
295
|
return;
|
|
292
296
|
}
|
|
293
297
|
|
|
@@ -307,7 +311,8 @@ const chemistryPhHelper = {
|
|
|
307
311
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
308
312
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'source_not_found');
|
|
309
313
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'source_not_found');
|
|
310
|
-
|
|
314
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
315
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
311
316
|
return;
|
|
312
317
|
}
|
|
313
318
|
|
|
@@ -320,7 +325,8 @@ const chemistryPhHelper = {
|
|
|
320
325
|
await this._setBool('chemistry.ph.evaluation.action_required', false);
|
|
321
326
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'unknown_source_mode');
|
|
322
327
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'unknown_source_mode');
|
|
323
|
-
|
|
328
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
329
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX
|
|
324
330
|
} catch (err) {
|
|
325
331
|
this.adapter.log.warn(`[chemistryPhHelper] Evaluation failed: ${err.message}`);
|
|
326
332
|
}
|
|
@@ -330,7 +336,7 @@ const chemistryPhHelper = {
|
|
|
330
336
|
const now = new Date();
|
|
331
337
|
const value = Number(rawValue);
|
|
332
338
|
|
|
333
|
-
await this.
|
|
339
|
+
await this._setNumber('chemistry.ph.input.last_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
334
340
|
|
|
335
341
|
if (source === 'manual') {
|
|
336
342
|
await this._setBool('chemistry.ph.input.source_valid', true);
|
|
@@ -355,7 +361,7 @@ const chemistryPhHelper = {
|
|
|
355
361
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
356
362
|
await this._setString('chemistry.ph.measurement.ignored_reason', 'invalid_value');
|
|
357
363
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'invalid_value');
|
|
358
|
-
await this.
|
|
364
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
359
365
|
return;
|
|
360
366
|
}
|
|
361
367
|
|
|
@@ -368,7 +374,7 @@ const chemistryPhHelper = {
|
|
|
368
374
|
await this._setBool('chemistry.ph.measurement.allowed', false);
|
|
369
375
|
await this._setString('chemistry.ph.measurement.ignored_reason', measurementAllowed.reason);
|
|
370
376
|
await this._setString('chemistry.ph.debug.last_reason', reason || measurementAllowed.reason);
|
|
371
|
-
await this.
|
|
377
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
372
378
|
return;
|
|
373
379
|
}
|
|
374
380
|
|
|
@@ -385,7 +391,7 @@ const chemistryPhHelper = {
|
|
|
385
391
|
await this._writeOutputs(value, trend, evaluation);
|
|
386
392
|
|
|
387
393
|
await this._setString('chemistry.ph.debug.last_reason', reason || 'value_processed');
|
|
388
|
-
await this.
|
|
394
|
+
await this._setNumber('chemistry.ph.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
389
395
|
},
|
|
390
396
|
|
|
391
397
|
async _checkMeasurementAllowed(now) {
|
|
@@ -442,22 +448,18 @@ const chemistryPhHelper = {
|
|
|
442
448
|
|
|
443
449
|
async _updateLastValues(value, now) {
|
|
444
450
|
const lastValid = await this._readNumberOrNull('chemistry.ph.input.last_valid_value');
|
|
445
|
-
const lastValidAt = await this.
|
|
451
|
+
const lastValidAt = await this._readTimestampOrNull('chemistry.ph.input.last_valid_value_at'); // FIX: accept numeric ms timestamps and legacy German strings.
|
|
446
452
|
|
|
447
453
|
if (lastValid !== null && lastValidAt) {
|
|
448
454
|
await this._setNumber('chemistry.ph.input.previous_value', lastValid);
|
|
449
|
-
await this.
|
|
455
|
+
await this._setNumber('chemistry.ph.input.previous_value_at', lastValidAt); // FIX: pass stored timestamp through unchanged.
|
|
450
456
|
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
if (previousDate) {
|
|
454
|
-
const minutes = Math.max(0, Math.round((now.getTime() - previousDate.getTime()) / 60000));
|
|
455
|
-
await this._setNumber('chemistry.ph.input.minutes_since_previous_value', minutes);
|
|
456
|
-
}
|
|
457
|
+
const minutes = Math.max(0, Math.round((now.getTime() - lastValidAt) / 60000)); // FIX: calculate from numeric ms timestamp.
|
|
458
|
+
await this._setNumber('chemistry.ph.input.minutes_since_previous_value', minutes);
|
|
457
459
|
}
|
|
458
460
|
|
|
459
461
|
await this._setNumber('chemistry.ph.input.last_valid_value', value);
|
|
460
|
-
await this.
|
|
462
|
+
await this._setNumber('chemistry.ph.input.last_valid_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
461
463
|
},
|
|
462
464
|
|
|
463
465
|
async _updateHistory(value, now, forceSample) {
|
|
@@ -487,13 +489,15 @@ const chemistryPhHelper = {
|
|
|
487
489
|
await this._setNumber('chemistry.ph.history.samples_count', samples.length);
|
|
488
490
|
|
|
489
491
|
if (samples.length) {
|
|
490
|
-
await this.
|
|
492
|
+
await this._setNumber(
|
|
493
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
491
494
|
'chemistry.ph.history.oldest_sample_at',
|
|
492
|
-
|
|
495
|
+
Number(samples[0].ts),
|
|
493
496
|
);
|
|
494
|
-
await this.
|
|
497
|
+
await this._setNumber(
|
|
498
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
495
499
|
'chemistry.ph.history.newest_sample_at',
|
|
496
|
-
|
|
500
|
+
Number(samples[samples.length - 1].ts),
|
|
497
501
|
);
|
|
498
502
|
}
|
|
499
503
|
|
|
@@ -601,15 +605,15 @@ const chemistryPhHelper = {
|
|
|
601
605
|
|
|
602
606
|
async _writeTrend(trend) {
|
|
603
607
|
await this._setNumber('chemistry.ph.trend.reference_24h_value', trend.ref24h ? trend.ref24h.value : 0);
|
|
604
|
-
await this.
|
|
608
|
+
await this._setNumber('chemistry.ph.trend.reference_24h_at', trend.ref24h ? trend.ref24h.ts : 0); // FIX
|
|
605
609
|
await this._setNumber('chemistry.ph.trend.delta_24h', trend.ref24h ? trend.delta24h : 0);
|
|
606
610
|
|
|
607
611
|
await this._setNumber('chemistry.ph.trend.reference_7d_value', trend.ref7d ? trend.ref7d.value : 0);
|
|
608
|
-
await this.
|
|
612
|
+
await this._setNumber('chemistry.ph.trend.reference_7d_at', trend.ref7d ? trend.ref7d.ts : 0); // FIX
|
|
609
613
|
await this._setNumber('chemistry.ph.trend.delta_7d', trend.ref7d ? trend.delta7d : 0);
|
|
610
614
|
|
|
611
615
|
await this._setNumber('chemistry.ph.trend.reference_30d_value', trend.ref30d ? trend.ref30d.value : 0);
|
|
612
|
-
await this.
|
|
616
|
+
await this._setNumber('chemistry.ph.trend.reference_30d_at', trend.ref30d ? trend.ref30d.ts : 0); // FIX
|
|
613
617
|
await this._setNumber('chemistry.ph.trend.delta_30d', trend.ref30d ? trend.delta30d : 0);
|
|
614
618
|
|
|
615
619
|
await this._setString('chemistry.ph.trend.direction', trend.direction);
|
|
@@ -869,6 +873,19 @@ const chemistryPhHelper = {
|
|
|
869
873
|
return Number.isFinite(value) ? value : null;
|
|
870
874
|
},
|
|
871
875
|
|
|
876
|
+
async _readTimestampOrNull(id) {
|
|
877
|
+
const state = await this.adapter.getStateAsync(id);
|
|
878
|
+
const value = state?.val;
|
|
879
|
+
const numberValue = Number(value);
|
|
880
|
+
|
|
881
|
+
if (Number.isFinite(numberValue) && numberValue > 0) {
|
|
882
|
+
return numberValue; // FIX: numeric value.time timestamps are used directly.
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const legacyDate = this._parseGermanDateTime(value); // FIX: keep backward compatibility for old string values.
|
|
886
|
+
return legacyDate ? legacyDate.getTime() : null;
|
|
887
|
+
},
|
|
888
|
+
|
|
872
889
|
async _readJsonArray(id) {
|
|
873
890
|
const state = await this.adapter.getStateAsync(id);
|
|
874
891
|
|
|
@@ -246,7 +246,7 @@ const chemistryTdsHelper = {
|
|
|
246
246
|
const now = new Date();
|
|
247
247
|
const value = Number(rawValue);
|
|
248
248
|
|
|
249
|
-
await this.
|
|
249
|
+
await this._setNumber('chemistry.tds.input.last_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
250
250
|
|
|
251
251
|
if (source === 'manual') {
|
|
252
252
|
await this._setBool('chemistry.tds.input.source_valid', true);
|
|
@@ -278,7 +278,7 @@ const chemistryTdsHelper = {
|
|
|
278
278
|
await this._setString('chemistry.tds.evaluation.recommendation', measurementAllowed.recommendation);
|
|
279
279
|
await this._setBool('chemistry.tds.evaluation.action_required', false);
|
|
280
280
|
await this._setString('chemistry.tds.debug.last_reason', reason || measurementAllowed.reason);
|
|
281
|
-
await this.
|
|
281
|
+
await this._setNumber('chemistry.tds.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
282
282
|
return;
|
|
283
283
|
}
|
|
284
284
|
|
|
@@ -297,7 +297,7 @@ const chemistryTdsHelper = {
|
|
|
297
297
|
await this._writeOutputs(value, reference, trend, evaluation);
|
|
298
298
|
|
|
299
299
|
await this._setString('chemistry.tds.debug.last_reason', reason || 'value_processed');
|
|
300
|
-
await this.
|
|
300
|
+
await this._setNumber('chemistry.tds.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
301
301
|
},
|
|
302
302
|
|
|
303
303
|
async _checkMeasurementAllowed(now) {
|
|
@@ -357,22 +357,18 @@ const chemistryTdsHelper = {
|
|
|
357
357
|
|
|
358
358
|
async _updateLastValues(value, now) {
|
|
359
359
|
const lastValid = await this._readNumberOrNull('chemistry.tds.input.last_valid_value');
|
|
360
|
-
const lastValidAt = await this.
|
|
360
|
+
const lastValidAt = await this._readTimestampOrNull('chemistry.tds.input.last_valid_value_at'); // FIX: accept numeric ms timestamps and legacy German strings.
|
|
361
361
|
|
|
362
362
|
if (lastValid !== null && lastValidAt) {
|
|
363
363
|
await this._setNumber('chemistry.tds.input.previous_value', lastValid);
|
|
364
|
-
await this.
|
|
364
|
+
await this._setNumber('chemistry.tds.input.previous_value_at', lastValidAt); // FIX: pass stored timestamp through unchanged.
|
|
365
365
|
|
|
366
|
-
const
|
|
367
|
-
|
|
368
|
-
if (previousDate) {
|
|
369
|
-
const minutes = Math.max(0, Math.round((now.getTime() - previousDate.getTime()) / 60000));
|
|
370
|
-
await this._setNumber('chemistry.tds.input.minutes_since_previous_value', minutes);
|
|
371
|
-
}
|
|
366
|
+
const minutes = Math.max(0, Math.round((now.getTime() - lastValidAt) / 60000)); // FIX: calculate from numeric ms timestamp.
|
|
367
|
+
await this._setNumber('chemistry.tds.input.minutes_since_previous_value', minutes);
|
|
372
368
|
}
|
|
373
369
|
|
|
374
370
|
await this._setNumber('chemistry.tds.input.last_valid_value', value);
|
|
375
|
-
await this.
|
|
371
|
+
await this._setNumber('chemistry.tds.input.last_valid_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
376
372
|
},
|
|
377
373
|
|
|
378
374
|
async _updateHistory(value, now, forceSample) {
|
|
@@ -402,13 +398,15 @@ const chemistryTdsHelper = {
|
|
|
402
398
|
await this._setNumber('chemistry.tds.history.samples_count', samples.length);
|
|
403
399
|
|
|
404
400
|
if (samples.length) {
|
|
405
|
-
await this.
|
|
401
|
+
await this._setNumber(
|
|
402
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
406
403
|
'chemistry.tds.history.oldest_sample_at',
|
|
407
|
-
|
|
404
|
+
Number(samples[0].ts),
|
|
408
405
|
);
|
|
409
|
-
await this.
|
|
406
|
+
await this._setNumber(
|
|
407
|
+
// FIX: history value.time states store sample timestamps, not readable strings.
|
|
410
408
|
'chemistry.tds.history.newest_sample_at',
|
|
411
|
-
|
|
409
|
+
Number(samples[samples.length - 1].ts),
|
|
412
410
|
);
|
|
413
411
|
}
|
|
414
412
|
|
|
@@ -424,7 +422,7 @@ const chemistryTdsHelper = {
|
|
|
424
422
|
initialSet = true;
|
|
425
423
|
|
|
426
424
|
await this._setNumber('chemistry.tds.reference.initial_value', initialValue);
|
|
427
|
-
await this.
|
|
425
|
+
await this._setNumber('chemistry.tds.reference.initial_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
428
426
|
await this._setBool('chemistry.tds.reference.initial_set', true);
|
|
429
427
|
}
|
|
430
428
|
|
|
@@ -450,11 +448,11 @@ const chemistryTdsHelper = {
|
|
|
450
448
|
const now = new Date();
|
|
451
449
|
|
|
452
450
|
await this._setNumber('chemistry.tds.reference.initial_value', currentValue);
|
|
453
|
-
await this.
|
|
451
|
+
await this._setNumber('chemistry.tds.reference.initial_value_at', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
454
452
|
await this._setBool('chemistry.tds.reference.initial_set', true);
|
|
455
453
|
await this._setNumber('chemistry.tds.reference.delta_since_initial', 0);
|
|
456
454
|
await this._setString('chemistry.tds.debug.last_reason', 'reference_reset');
|
|
457
|
-
await this.
|
|
455
|
+
await this._setNumber('chemistry.tds.debug.last_update', now.getTime()); // FIX: value.time uses numeric ms timestamp.
|
|
458
456
|
|
|
459
457
|
this._scheduleEvaluation('reference_reset', 500);
|
|
460
458
|
},
|
|
@@ -560,15 +558,15 @@ const chemistryTdsHelper = {
|
|
|
560
558
|
|
|
561
559
|
async _writeTrend(trend) {
|
|
562
560
|
await this._setNumber('chemistry.tds.trend.reference_24h_value', trend.ref24h ? trend.ref24h.value : 0);
|
|
563
|
-
await this.
|
|
561
|
+
await this._setNumber('chemistry.tds.trend.reference_24h_at', trend.ref24h ? trend.ref24h.ts : 0); // FIX
|
|
564
562
|
await this._setNumber('chemistry.tds.trend.delta_24h', trend.ref24h ? trend.delta24h : 0);
|
|
565
563
|
|
|
566
564
|
await this._setNumber('chemistry.tds.trend.reference_7d_value', trend.ref7d ? trend.ref7d.value : 0);
|
|
567
|
-
await this.
|
|
565
|
+
await this._setNumber('chemistry.tds.trend.reference_7d_at', trend.ref7d ? trend.ref7d.ts : 0); // FIX
|
|
568
566
|
await this._setNumber('chemistry.tds.trend.delta_7d', trend.ref7d ? trend.delta7d : 0);
|
|
569
567
|
|
|
570
568
|
await this._setNumber('chemistry.tds.trend.reference_30d_value', trend.ref30d ? trend.ref30d.value : 0);
|
|
571
|
-
await this.
|
|
569
|
+
await this._setNumber('chemistry.tds.trend.reference_30d_at', trend.ref30d ? trend.ref30d.ts : 0); // FIX
|
|
572
570
|
await this._setNumber('chemistry.tds.trend.delta_30d', trend.ref30d ? trend.delta30d : 0);
|
|
573
571
|
|
|
574
572
|
await this._setString('chemistry.tds.trend.direction', trend.direction);
|
|
@@ -718,7 +716,8 @@ const chemistryTdsHelper = {
|
|
|
718
716
|
await this._setString('chemistry.tds.evaluation.recommendation', I18n.translate('TDS evaluation is disabled.'));
|
|
719
717
|
await this._setBool('chemistry.tds.evaluation.action_required', false);
|
|
720
718
|
await this._setString('chemistry.tds.debug.last_reason', reason);
|
|
721
|
-
|
|
719
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
720
|
+
await this._setNumber('chemistry.tds.debug.last_update', now.getTime()); // FIX
|
|
722
721
|
},
|
|
723
722
|
|
|
724
723
|
async _writeInvalid(recommendation, reason) {
|
|
@@ -730,7 +729,8 @@ const chemistryTdsHelper = {
|
|
|
730
729
|
await this._setString('chemistry.tds.evaluation.recommendation', recommendation);
|
|
731
730
|
await this._setBool('chemistry.tds.evaluation.action_required', false);
|
|
732
731
|
await this._setString('chemistry.tds.debug.last_reason', reason);
|
|
733
|
-
|
|
732
|
+
const now = new Date(); // FIX: write value.time as numeric Unix timestamp in milliseconds.
|
|
733
|
+
await this._setNumber('chemistry.tds.debug.last_update', now.getTime()); // FIX
|
|
734
734
|
},
|
|
735
735
|
|
|
736
736
|
async _readString(id) {
|
|
@@ -750,6 +750,19 @@ const chemistryTdsHelper = {
|
|
|
750
750
|
return Number.isFinite(value) ? value : null;
|
|
751
751
|
},
|
|
752
752
|
|
|
753
|
+
async _readTimestampOrNull(id) {
|
|
754
|
+
const state = await this.adapter.getStateAsync(id);
|
|
755
|
+
const value = state?.val;
|
|
756
|
+
const numberValue = Number(value);
|
|
757
|
+
|
|
758
|
+
if (Number.isFinite(numberValue) && numberValue > 0) {
|
|
759
|
+
return numberValue; // FIX: numeric value.time timestamps are used directly.
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const legacyDate = this._parseGermanDateTime(value); // FIX: keep backward compatibility for old string values.
|
|
763
|
+
return legacyDate ? legacyDate.getTime() : null;
|
|
764
|
+
},
|
|
765
|
+
|
|
753
766
|
async _readBoolean(id) {
|
|
754
767
|
const state = await this.adapter.getStateAsync(id);
|
|
755
768
|
return !!state?.val;
|
|
@@ -21,6 +21,7 @@ const runtimeHelper = {
|
|
|
21
21
|
restoreTimer: null, // FIX: ioBroker timer for delayed restore
|
|
22
22
|
resetTimer: null,
|
|
23
23
|
liveTimer: null, // Timer für Live-Updates
|
|
24
|
+
syncTimer: null, // FIX: Timer zur Selbstheilung bei verpasstem Pumpenstart/-stopp
|
|
24
25
|
|
|
25
26
|
/**
|
|
26
27
|
* Initialisiert den Runtime-Helper.
|
|
@@ -54,6 +55,9 @@ const runtimeHelper = {
|
|
|
54
55
|
// Tagesreset einplanen
|
|
55
56
|
this._scheduleDailyReset();
|
|
56
57
|
|
|
58
|
+
// FIX: Start sync timer to recover from missed pump start/stop events
|
|
59
|
+
this._startSyncTimer();
|
|
60
|
+
|
|
57
61
|
// Erst nach Restore einmal berechnen
|
|
58
62
|
this._updateStates();
|
|
59
63
|
|
|
@@ -62,6 +66,10 @@ const runtimeHelper = {
|
|
|
62
66
|
.catch(err => {
|
|
63
67
|
this.adapter.log.warn(`[runtimeHelper] Restore failed: ${err.message}`);
|
|
64
68
|
this._scheduleDailyReset();
|
|
69
|
+
|
|
70
|
+
// FIX: Start sync timer to recover from missed pump start/stop events
|
|
71
|
+
this._startSyncTimer();
|
|
72
|
+
|
|
65
73
|
this._updateStates();
|
|
66
74
|
this.adapter.log.debug('[runtimeHelper] Initialized (without restore)');
|
|
67
75
|
});
|
|
@@ -112,7 +120,7 @@ const runtimeHelper = {
|
|
|
112
120
|
this.lastOn = Date.now();
|
|
113
121
|
this.startCountToday += 1;
|
|
114
122
|
|
|
115
|
-
// Live-Timer starten (
|
|
123
|
+
// Live-Timer starten (alle 10 Sekunden)
|
|
116
124
|
this._startLiveTimer();
|
|
117
125
|
|
|
118
126
|
// Start sofort in State schreiben
|
|
@@ -389,6 +397,43 @@ const runtimeHelper = {
|
|
|
389
397
|
}
|
|
390
398
|
},
|
|
391
399
|
|
|
400
|
+
_startSyncTimer() {
|
|
401
|
+
if (this.syncTimer) {
|
|
402
|
+
this.adapter.clearInterval(this.syncTimer);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
this.syncTimer = this.adapter.setInterval(() => this._syncPumpRuntimeState(), 30 * 1000);
|
|
406
|
+
this.adapter.log.debug('[runtimeHelper] FIX: Runtime sync timer started (checks every 30 seconds)');
|
|
407
|
+
},
|
|
408
|
+
|
|
409
|
+
async _syncPumpRuntimeState() {
|
|
410
|
+
try {
|
|
411
|
+
const pumpState = await this.adapter.getStateAsync('pump.pump_switch');
|
|
412
|
+
const pumpOn = pumpState?.val === true;
|
|
413
|
+
|
|
414
|
+
if (pumpOn && (!this.isRunning || !this.lastOn)) {
|
|
415
|
+
this.adapter.log.debug(
|
|
416
|
+
'[runtimeHelper] FIX: Pump switch is ON but runtime was not running. Synchronizing runtime start.',
|
|
417
|
+
);
|
|
418
|
+
await this.handleStateChange('pump.pump_switch', { val: true, ack: true });
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// FIX: Stop synchronization is intentionally not handled here.
|
|
423
|
+
// A missed stop event could otherwise cause duplicate runtime accounting
|
|
424
|
+
// during parallel async stop handling. Normal stop events are still handled
|
|
425
|
+
// through handleStateChange().
|
|
426
|
+
// if (!pumpOn && (this.isRunning || this.lastOn)) {
|
|
427
|
+
// this.adapter.log.debug(
|
|
428
|
+
// '[runtimeHelper] FIX: Pump switch is OFF but runtime was still running. Synchronizing runtime stop.',
|
|
429
|
+
// );
|
|
430
|
+
// await this.handleStateChange('pump.pump_switch', { val: false, ack: true });
|
|
431
|
+
// }
|
|
432
|
+
} catch (err) {
|
|
433
|
+
this.adapter.log.warn(`[runtimeHelper] Error while synchronizing pump runtime state: ${err.message}`);
|
|
434
|
+
}
|
|
435
|
+
},
|
|
436
|
+
|
|
392
437
|
cleanup() {
|
|
393
438
|
if (this.restoreTimer) {
|
|
394
439
|
this.adapter.clearTimeout(this.restoreTimer);
|
|
@@ -400,6 +445,11 @@ const runtimeHelper = {
|
|
|
400
445
|
this.resetTimer = null;
|
|
401
446
|
}
|
|
402
447
|
|
|
448
|
+
if (this.syncTimer) {
|
|
449
|
+
this.adapter.clearInterval(this.syncTimer);
|
|
450
|
+
this.syncTimer = null;
|
|
451
|
+
}
|
|
452
|
+
|
|
403
453
|
this._stopLiveTimer();
|
|
404
454
|
},
|
|
405
455
|
};
|
|
@@ -214,11 +214,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
214
214
|
en: 'Readable date and time when the last ORP value was received.',
|
|
215
215
|
de: 'Lesbares Datum und Uhrzeit, wann der letzte ORP-Wert empfangen wurde.',
|
|
216
216
|
},
|
|
217
|
-
type: '
|
|
217
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
218
218
|
role: 'value.time',
|
|
219
219
|
read: true,
|
|
220
220
|
write: false,
|
|
221
|
-
def:
|
|
221
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
222
222
|
persist: true,
|
|
223
223
|
});
|
|
224
224
|
|
|
@@ -251,11 +251,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
251
251
|
en: 'Readable date and time of the last valid ORP value.',
|
|
252
252
|
de: 'Lesbares Datum und Uhrzeit des letzten gültigen ORP-Werts.',
|
|
253
253
|
},
|
|
254
|
-
type: '
|
|
254
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
255
255
|
role: 'value.time',
|
|
256
256
|
read: true,
|
|
257
257
|
write: false,
|
|
258
|
-
def:
|
|
258
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
259
259
|
persist: true,
|
|
260
260
|
});
|
|
261
261
|
|
|
@@ -288,11 +288,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
288
288
|
en: 'Readable date and time of the previous valid ORP value.',
|
|
289
289
|
de: 'Lesbares Datum und Uhrzeit des vorherigen gültigen ORP-Werts.',
|
|
290
290
|
},
|
|
291
|
-
type: '
|
|
291
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
292
292
|
role: 'value.time',
|
|
293
293
|
read: true,
|
|
294
294
|
write: false,
|
|
295
|
-
def:
|
|
295
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
296
296
|
persist: true,
|
|
297
297
|
});
|
|
298
298
|
|
|
@@ -685,11 +685,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
685
685
|
en: 'Readable timestamp of the 24h ORP reference value.',
|
|
686
686
|
de: 'Lesbarer Zeitstempel des 24h-ORP-Referenzwerts.',
|
|
687
687
|
},
|
|
688
|
-
type: '
|
|
688
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
689
689
|
role: 'value.time',
|
|
690
690
|
read: true,
|
|
691
691
|
write: false,
|
|
692
|
-
def:
|
|
692
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
693
693
|
persist: true,
|
|
694
694
|
});
|
|
695
695
|
|
|
@@ -738,11 +738,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
738
738
|
en: 'Readable timestamp of the 7 day ORP reference value.',
|
|
739
739
|
de: 'Lesbarer Zeitstempel des 7-Tage-ORP-Referenzwerts.',
|
|
740
740
|
},
|
|
741
|
-
type: '
|
|
741
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
742
742
|
role: 'value.time',
|
|
743
743
|
read: true,
|
|
744
744
|
write: false,
|
|
745
|
-
def:
|
|
745
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
746
746
|
persist: true,
|
|
747
747
|
});
|
|
748
748
|
|
|
@@ -791,11 +791,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
791
791
|
en: 'Readable timestamp of the 30 day ORP reference value.',
|
|
792
792
|
de: 'Lesbarer Zeitstempel des 30-Tage-ORP-Referenzwerts.',
|
|
793
793
|
},
|
|
794
|
-
type: '
|
|
794
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
795
795
|
role: 'value.time',
|
|
796
796
|
read: true,
|
|
797
797
|
write: false,
|
|
798
|
-
def:
|
|
798
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
799
799
|
persist: true,
|
|
800
800
|
});
|
|
801
801
|
|
|
@@ -934,11 +934,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
934
934
|
en: 'Readable timestamp of the oldest stored ORP sample.',
|
|
935
935
|
de: 'Lesbarer Zeitstempel des ältesten gespeicherten ORP-Messwerts.',
|
|
936
936
|
},
|
|
937
|
-
type: '
|
|
937
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
938
938
|
role: 'value.time',
|
|
939
939
|
read: true,
|
|
940
940
|
write: false,
|
|
941
|
-
def:
|
|
941
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
942
942
|
persist: true,
|
|
943
943
|
});
|
|
944
944
|
|
|
@@ -951,11 +951,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
951
951
|
en: 'Readable timestamp of the newest stored ORP sample.',
|
|
952
952
|
de: 'Lesbarer Zeitstempel des neuesten gespeicherten ORP-Messwerts.',
|
|
953
953
|
},
|
|
954
|
-
type: '
|
|
954
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
955
955
|
role: 'value.time',
|
|
956
956
|
read: true,
|
|
957
957
|
write: false,
|
|
958
|
-
def:
|
|
958
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
959
959
|
persist: true,
|
|
960
960
|
});
|
|
961
961
|
|
|
@@ -1035,11 +1035,11 @@ async function createChemistryOrpStates(adapter) {
|
|
|
1035
1035
|
en: 'Readable timestamp of the last ORP evaluation update.',
|
|
1036
1036
|
de: 'Lesbarer Zeitstempel der letzten ORP-Auswertung.',
|
|
1037
1037
|
},
|
|
1038
|
-
type: '
|
|
1038
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
1039
1039
|
role: 'value.time',
|
|
1040
1040
|
read: true,
|
|
1041
1041
|
write: false,
|
|
1042
|
-
def:
|
|
1042
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
1043
1043
|
persist: true,
|
|
1044
1044
|
});
|
|
1045
1045
|
|
|
@@ -209,11 +209,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
209
209
|
en: 'Readable date and time when the last pH value was received.',
|
|
210
210
|
de: 'Lesbares Datum und Uhrzeit, wann der letzte pH-Wert empfangen wurde.',
|
|
211
211
|
},
|
|
212
|
-
type: '
|
|
212
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
213
213
|
role: 'value.time',
|
|
214
214
|
read: true,
|
|
215
215
|
write: false,
|
|
216
|
-
def:
|
|
216
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
217
217
|
persist: true,
|
|
218
218
|
});
|
|
219
219
|
|
|
@@ -245,11 +245,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
245
245
|
en: 'Readable date and time of the last valid pH value.',
|
|
246
246
|
de: 'Lesbares Datum und Uhrzeit des letzten gültigen pH-Werts.',
|
|
247
247
|
},
|
|
248
|
-
type: '
|
|
248
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
249
249
|
role: 'value.time',
|
|
250
250
|
read: true,
|
|
251
251
|
write: false,
|
|
252
|
-
def:
|
|
252
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
253
253
|
persist: true,
|
|
254
254
|
});
|
|
255
255
|
|
|
@@ -281,11 +281,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
281
281
|
en: 'Readable date and time of the previous valid pH value.',
|
|
282
282
|
de: 'Lesbares Datum und Uhrzeit des vorherigen gültigen pH-Werts.',
|
|
283
283
|
},
|
|
284
|
-
type: '
|
|
284
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
285
285
|
role: 'value.time',
|
|
286
286
|
read: true,
|
|
287
287
|
write: false,
|
|
288
|
-
def:
|
|
288
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
289
289
|
persist: true,
|
|
290
290
|
});
|
|
291
291
|
|
|
@@ -698,11 +698,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
698
698
|
en: 'Oldest sample time',
|
|
699
699
|
de: 'Ältester Messwert',
|
|
700
700
|
},
|
|
701
|
-
type: '
|
|
701
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
702
702
|
role: 'value.time',
|
|
703
703
|
read: true,
|
|
704
704
|
write: false,
|
|
705
|
-
def:
|
|
705
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
706
706
|
persist: true,
|
|
707
707
|
});
|
|
708
708
|
|
|
@@ -711,11 +711,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
711
711
|
en: 'Newest sample time',
|
|
712
712
|
de: 'Neuester Messwert',
|
|
713
713
|
},
|
|
714
|
-
type: '
|
|
714
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
715
715
|
role: 'value.time',
|
|
716
716
|
read: true,
|
|
717
717
|
write: false,
|
|
718
|
-
def:
|
|
718
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
719
719
|
persist: true,
|
|
720
720
|
});
|
|
721
721
|
|
|
@@ -763,11 +763,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
763
763
|
en: enName,
|
|
764
764
|
de: deName,
|
|
765
765
|
},
|
|
766
|
-
type: '
|
|
766
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
767
767
|
role: 'value.time',
|
|
768
768
|
read: true,
|
|
769
769
|
write: false,
|
|
770
|
-
def:
|
|
770
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
771
771
|
persist: true,
|
|
772
772
|
});
|
|
773
773
|
}
|
|
@@ -882,11 +882,11 @@ async function createChemistryPhStates(adapter) {
|
|
|
882
882
|
en: 'Readable timestamp of the last pH evaluation update.',
|
|
883
883
|
de: 'Lesbarer Zeitstempel der letzten pH-Auswertung.',
|
|
884
884
|
},
|
|
885
|
-
type: '
|
|
885
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
886
886
|
role: 'value.time',
|
|
887
887
|
read: true,
|
|
888
888
|
write: false,
|
|
889
|
-
def:
|
|
889
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
890
890
|
persist: true,
|
|
891
891
|
});
|
|
892
892
|
|
|
@@ -211,11 +211,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
211
211
|
en: 'Readable date and time when the last TDS value was received.',
|
|
212
212
|
de: 'Lesbares Datum und Uhrzeit, wann der letzte TDS-Wert empfangen wurde.',
|
|
213
213
|
},
|
|
214
|
-
type: '
|
|
214
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
215
215
|
role: 'value.time',
|
|
216
216
|
read: true,
|
|
217
217
|
write: false,
|
|
218
|
-
def:
|
|
218
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
219
219
|
persist: true,
|
|
220
220
|
});
|
|
221
221
|
|
|
@@ -247,11 +247,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
247
247
|
en: 'Readable date and time of the last valid TDS value.',
|
|
248
248
|
de: 'Lesbares Datum und Uhrzeit des letzten gültigen TDS-Werts.',
|
|
249
249
|
},
|
|
250
|
-
type: '
|
|
250
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
251
251
|
role: 'value.time',
|
|
252
252
|
read: true,
|
|
253
253
|
write: false,
|
|
254
|
-
def:
|
|
254
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
255
255
|
persist: true,
|
|
256
256
|
});
|
|
257
257
|
|
|
@@ -283,11 +283,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
283
283
|
en: 'Readable date and time of the previous valid TDS value.',
|
|
284
284
|
de: 'Lesbares Datum und Uhrzeit des vorherigen gültigen TDS-Werts.',
|
|
285
285
|
},
|
|
286
|
-
type: '
|
|
286
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
287
287
|
role: 'value.time',
|
|
288
288
|
read: true,
|
|
289
289
|
write: false,
|
|
290
|
-
def:
|
|
290
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
291
291
|
persist: true,
|
|
292
292
|
});
|
|
293
293
|
|
|
@@ -480,11 +480,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
480
480
|
en: 'Readable date and time when the initial reference value was set.',
|
|
481
481
|
de: 'Lesbares Datum und Uhrzeit, wann der Referenzwert gesetzt wurde.',
|
|
482
482
|
},
|
|
483
|
-
type: '
|
|
483
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
484
484
|
role: 'value.time',
|
|
485
485
|
read: true,
|
|
486
486
|
write: false,
|
|
487
|
-
def:
|
|
487
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
488
488
|
persist: true,
|
|
489
489
|
});
|
|
490
490
|
|
|
@@ -584,11 +584,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
584
584
|
en: enName,
|
|
585
585
|
de: deName,
|
|
586
586
|
},
|
|
587
|
-
type: '
|
|
587
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
588
588
|
role: 'value.time',
|
|
589
589
|
read: true,
|
|
590
590
|
write: false,
|
|
591
|
-
def:
|
|
591
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
592
592
|
persist: true,
|
|
593
593
|
});
|
|
594
594
|
}
|
|
@@ -767,11 +767,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
767
767
|
en: 'Oldest sample time',
|
|
768
768
|
de: 'Ältester Messwert',
|
|
769
769
|
},
|
|
770
|
-
type: '
|
|
770
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
771
771
|
role: 'value.time',
|
|
772
772
|
read: true,
|
|
773
773
|
write: false,
|
|
774
|
-
def:
|
|
774
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
775
775
|
persist: true,
|
|
776
776
|
});
|
|
777
777
|
|
|
@@ -780,11 +780,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
780
780
|
en: 'Newest sample time',
|
|
781
781
|
de: 'Neuester Messwert',
|
|
782
782
|
},
|
|
783
|
-
type: '
|
|
783
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
784
784
|
role: 'value.time',
|
|
785
785
|
read: true,
|
|
786
786
|
write: false,
|
|
787
|
-
def:
|
|
787
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
788
788
|
persist: true,
|
|
789
789
|
});
|
|
790
790
|
|
|
@@ -860,11 +860,11 @@ async function createChemistryTdsStates(adapter) {
|
|
|
860
860
|
en: 'Last update',
|
|
861
861
|
de: 'Letzte Aktualisierung',
|
|
862
862
|
},
|
|
863
|
-
type: '
|
|
863
|
+
type: 'number', // FIX: ioBroker value.time states must store Unix timestamps in milliseconds.
|
|
864
864
|
role: 'value.time',
|
|
865
865
|
read: true,
|
|
866
866
|
write: false,
|
|
867
|
-
def:
|
|
867
|
+
def: 0, // FIX: numeric timestamp default for value.time.
|
|
868
868
|
persist: true,
|
|
869
869
|
});
|
|
870
870
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.poolcontrol",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.19",
|
|
4
4
|
"description": "Steuerung & Automatisierung für den Pool (Pumpe, Heizung, Ventile, Sensoren).",
|
|
5
5
|
"author": "DasBo1975 <dasbo1975@outlook.de>",
|
|
6
6
|
"homepage": "https://github.com/DasBo1975/ioBroker.poolcontrol",
|