iobroker.poolcontrol 1.3.32 → 1.3.34
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 +28 -14
- package/admin/i18n/uk.json +5 -5
- package/io-package.json +27 -27
- package/lib/helpers/chemistryOrpHelper.js +279 -17
- package/lib/helpers/chemistryPhHelper.js +279 -17
- package/lib/helpers/chemistryTdsHelper.js +279 -17
- package/lib/helpers/debugLogHelper.js +17 -2
- package/lib/helpers/runtimeHelper.js +22 -0
- package/lib/helpers/solarExtendedHelper.js +29 -0
- package/lib/helpers/solarHelper.js +20 -2
- package/lib/helpers/solarLogbookHelper.js +34 -2
- package/lib/i18n/de.json +2 -1
- package/lib/i18n/en.json +2 -1
- package/lib/i18n/es.json +2 -1
- package/lib/i18n/fr.json +2 -1
- package/lib/i18n/it.json +2 -1
- package/lib/i18n/nl.json +2 -1
- package/lib/i18n/pl.json +2 -1
- package/lib/i18n/pt.json +2 -1
- package/lib/i18n/ru.json +2 -1
- package/lib/i18n/uk.json +2 -1
- package/lib/i18n/zh-cn.json +2 -1
- package/lib/stateDefinitions/chemistryOrpStates.js +19 -2
- package/lib/stateDefinitions/chemistryPhStates.js +19 -2
- package/lib/stateDefinitions/chemistryTdsStates.js +19 -2
- package/lib/stateDefinitions/runtimeStates.js +13 -1
- package/lib/stateDefinitions/solarExtendedStates.js +22 -0
- package/lib/stateDefinitions/solarStates.js +23 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -36,6 +36,7 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
|
|
|
36
36
|
|
|
37
37
|
- **Solar Control**
|
|
38
38
|
- Collector on/off thresholds with hysteresis
|
|
39
|
+
- Live collector-surface delta for dashboards and scripts
|
|
39
40
|
- Collector warning threshold
|
|
40
41
|
- Optional speech output for warnings
|
|
41
42
|
- Automatic reset logic
|
|
@@ -43,10 +44,12 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
|
|
|
43
44
|
- **Solar Extended**
|
|
44
45
|
- Separate control for external solar actuators
|
|
45
46
|
- Delta on/off thresholds
|
|
47
|
+
- Live collector-pool reference delta for dashboards and scripts
|
|
46
48
|
- Maximum pool temperature limits
|
|
47
49
|
- Diagnostic and reason states
|
|
48
50
|
- Priority and block logic
|
|
49
51
|
- Status section under `solar.extended.*`
|
|
52
|
+
- Runtime changes to `solar.extended.pool_temperature_source` are applied automatically; because Solar Extended uses a cyclic check interval, calculation, control logic, and `solar.extended.collector_pool_reference_delta` may take up to approximately 60 seconds to update.
|
|
50
53
|
|
|
51
54
|
- **Photovoltaic Control**
|
|
52
55
|
- Pump control based on PV surplus and household consumption
|
|
@@ -184,6 +187,15 @@ It provides automation for pumps, heating, solar and photovoltaic control as wel
|
|
|
184
187
|
- No chlorine control
|
|
185
188
|
- No automatic dosing
|
|
186
189
|
|
|
190
|
+
**Two-tier bounded chemistry history**
|
|
191
|
+
- Existing samples_json states remain the 7-day, 15-minute short-term history: at most 672 samples and 64 KB each
|
|
192
|
+
- New internal daily_json states keep compact local-calendar-day aggregates with min/max/avg/last/count: at most 32 entries and 8 KB each
|
|
193
|
+
- 24h and 7d trends use samples_json; 30d trends use the last value of the matching daily aggregate
|
|
194
|
+
- Existing 24h, 7d, and 30d trend states and text/HTML/JSON reports retain their API and meaning
|
|
195
|
+
- The daily aggregates complement but do not replace raw history; valid legacy data is normalized during migration and oversized JSON is rejected before parsing
|
|
196
|
+
- Raw long-term histories belong in a dedicated ioBroker history/time-series database
|
|
197
|
+
- If an oversized states.jsonl already prevents js-controller startup, it must be repaired externally before PoolControl can run
|
|
198
|
+
|
|
187
199
|
**Chemistry Tools**
|
|
188
200
|
- pH Plus calculator
|
|
189
201
|
- pH Minus calculator
|
|
@@ -271,6 +283,22 @@ New features are added regularly – please refer to the changelog.
|
|
|
271
283
|
---
|
|
272
284
|
|
|
273
285
|
## Changelog
|
|
286
|
+
### 1.3.34 (2026-06-27)
|
|
287
|
+
|
|
288
|
+
- **Major stability improvement:** Completely redesigned the internal chemistry history (pH, ORP and TDS) to prevent unbounded JSON state growth. This significantly reduces the risk of oversized `states.jsonl` files and potential js-controller startup failures.
|
|
289
|
+
- **New two-stage history architecture:** Chemistry history now uses a compact short-term history for recent measurements together with a dedicated daily history for long-term trends. All existing 24-hour, 7-day and 30-day trend calculations and reports remain fully available.
|
|
290
|
+
- **Protected history storage:** Added strict limits for chemistry history sample count and JSON size. Oversized or invalid history states are now safely detected, validated and handled before being processed.
|
|
291
|
+
- **Daily aggregates introduced:** Added compact daily aggregates for pH, ORP and TDS containing minimum, maximum, average and last measurement together with the number of valid samples. This preserves long-term trend analysis without storing large raw histories.
|
|
292
|
+
- **Additional safeguards:** Added size protection for the solar logbook and debug log to prevent uncontrolled state growth.
|
|
293
|
+
- **Maintenance:** Updated the `@iobroker/adapter-core` dependency to the latest recommended version.
|
|
294
|
+
|
|
295
|
+
### 1.3.33 (2026-06-18)
|
|
296
|
+
|
|
297
|
+
- Added live delta states for standard solar and Solar Extended control:
|
|
298
|
+
- `solar.collector_surface_delta`
|
|
299
|
+
- `solar.extended.collector_pool_reference_delta`
|
|
300
|
+
- Documented that runtime changes to `solar.extended.pool_temperature_source` are applied automatically, but may take up to approximately 60 seconds due to the Solar Extended cyclic check interval.
|
|
301
|
+
|
|
274
302
|
### 1.3.32 (2026-06-08)
|
|
275
303
|
|
|
276
304
|
- Added circulation plausibility diagnostics for daily circulation calculations.
|
|
@@ -288,20 +316,6 @@ New features are added regularly – please refer to the changelog.
|
|
|
288
316
|
- Cleaned up outdated Admin i18n keys.
|
|
289
317
|
- Replaced native timers in AI weather helpers with ioBroker adapter timers.
|
|
290
318
|
|
|
291
|
-
### 1.3.29 (2026-06-04)
|
|
292
|
-
|
|
293
|
-
- Added Pool Insights V1 with observations, status evaluation and runtime i18n support.
|
|
294
|
-
- Improved Pool Insights text generation and removed dependency on external summary blocks.
|
|
295
|
-
- Added configurable startup power check timeout state for pump startup monitoring.
|
|
296
|
-
- Fixed missing initialization of season and solar warning runtime states from adapter configuration.
|
|
297
|
-
- Added admin UI information about initial configuration values and runtime datapoint control.
|
|
298
|
-
|
|
299
|
-
### 1.3.28 (2026-06-03)
|
|
300
|
-
|
|
301
|
-
- Added configurable startup power check timeout for pump monitoring (`pump.startup_power_check_timeout_sec`).
|
|
302
|
-
- Default behavior remains unchanged (5 seconds).
|
|
303
|
-
- Improved compatibility with delayed power measurements from smart plugs and power meters.
|
|
304
|
-
|
|
305
319
|
## Archived Release History
|
|
306
320
|
|
|
307
321
|
For older releases and archived version history see:
|
package/admin/i18n/uk.json
CHANGED
|
@@ -88,12 +88,12 @@
|
|
|
88
88
|
"Object ID Auxiliary pump 3": "Ідентифікатор об’єкта Допоміжний насос 3",
|
|
89
89
|
"Object ID Collector Sensor": "Датчик збирача ідентифікаторів об’єктів",
|
|
90
90
|
"Object ID Current Power (W)": "Ідентифікатор об'єкта Поточна потужність (Вт)",
|
|
91
|
-
"Object ID Flow Sensor": "
|
|
92
|
-
"Object ID Ground Sensor": "
|
|
91
|
+
"Object ID Flow Sensor": "Ідентифікатор об'єкта датчика потоку",
|
|
92
|
+
"Object ID Ground Sensor": "Ідентифікатор об'єкта датчика температури дна",
|
|
93
93
|
"Object ID Heating / Heat Pump": "Ідентифікатор об'єкта Опалення / Тепловий насос",
|
|
94
|
-
"Object ID Lighting 1": "
|
|
95
|
-
"Object ID Lighting 2": "
|
|
96
|
-
"Object ID Lighting 3": "
|
|
94
|
+
"Object ID Lighting 1": "Ідентифікатор об'єкта освітлення 1",
|
|
95
|
+
"Object ID Lighting 2": "Ідентифікатор об'єкта Освітлення 2",
|
|
96
|
+
"Object ID Lighting 3": "Ідентифікатор об'єкта Освітлення 3",
|
|
97
97
|
"Object ID Outside Temperature Sensor": "Ідентифікатор об’єкта Датчик зовнішньої температури",
|
|
98
98
|
"Object ID PV generation power (W)": "Ідентифікатор об'єкта Потужність генерації PV (Вт)",
|
|
99
99
|
"Object ID Pressure Sensor (bar)": "Ідентифікатор об'єкта Датчик тиску (бар)",
|
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.34",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.3.34": {
|
|
7
|
+
"en": "Improved chemistry history architecture with protected short-term and daily history, prevented oversized JSON states, added safeguards against state database growth, enhanced solar logbook/debug log protection, and updated adapter-core dependency.",
|
|
8
|
+
"de": "Chemie-Historie grundlegend verbessert: geschützte Kurzzeit- und Tageshistorie eingeführt, übergroße JSON-States verhindert, Schutz vor unkontrolliertem Wachstum der State-Datenbank ergänzt, Solar-Logbuch und Debug-Log zusätzlich abgesichert sowie adapter-core-Abhängigkeit aktualisiert.",
|
|
9
|
+
"ru": "Улучшенная архитектура истории химических процессов с защищенной краткосрочной и ежедневной историей, предотвращение чрезмерного размера состояний JSON, добавлена защита от роста базы данных состояний, улучшена защита журнала солнечной энергии/журнала отладки, а также обновлена зависимость от ядра адаптера.",
|
|
10
|
+
"pt": "Arquitetura de histórico químico aprimorada com histórico diário e de curto prazo protegido, prevenção de estados JSON superdimensionados, salvaguardas adicionais contra o crescimento do banco de dados estadual, proteção aprimorada de logbook solar/log de depuração e dependência atualizada do núcleo do adaptador.",
|
|
11
|
+
"nl": "Verbeterde architectuur van de chemiegeschiedenis met beschermde kortetermijn- en dagelijkse geschiedenis, voorkoming van te grote JSON-statussen, extra beveiliging tegen de groei van de staatsdatabase, verbeterde bescherming van het zonne-logboek/debug-logboek en bijgewerkte adapter-core-afhankelijkheid.",
|
|
12
|
+
"fr": "Architecture d'historique chimique améliorée avec historique protégé à court terme et quotidien, prévention des états JSON surdimensionnés, protections supplémentaires contre la croissance de la base de données d'état, protection améliorée du journal solaire/journal de débogage et dépendance mise à jour du noyau de l'adaptateur.",
|
|
13
|
+
"it": "Architettura della cronologia chimica migliorata con cronologia protetta a breve termine e giornaliera, prevenzione di stati JSON sovradimensionati, protezioni aggiuntive contro la crescita del database di stato, protezione migliorata del registro solare/registro di debug e dipendenza aggiornata del core dell'adattatore.",
|
|
14
|
+
"es": "Se mejoró la arquitectura del historial químico con historial diario y a corto plazo protegido, se evitaron estados JSON de gran tamaño, se agregaron salvaguardias contra el crecimiento de la base de datos estatal, se mejoró la protección del registro solar/registro de depuración y se actualizó la dependencia del núcleo del adaptador.",
|
|
15
|
+
"pl": "Ulepszona architektura historii chemii z chronioną historią krótkoterminową i codzienną, zapobieganie nadmiernym stanom JSON, dodane zabezpieczenia przed rozrostem bazy danych stanu, ulepszona ochrona dziennika solarnego/dziennika debugowania oraz zaktualizowana zależność od rdzenia adaptera.",
|
|
16
|
+
"uk": "Покращена архітектура хімічної історії із захищеною короткостроковою та щоденною історією, запобіганням надмірним станам JSON, доданим запобіжним засобам проти зростання бази даних стану, покращеному захисту сонячного журналу/журналу налагодження та оновленій залежності ядра адаптера.",
|
|
17
|
+
"zh-cn": "改进了化学历史架构,具有受保护的短期和每日历史记录,防止过大的 JSON 状态,增加了针对状态数据库增长的保护措施,增强了太阳能日志/调试日志保护,并更新了适配器核心依赖性。"
|
|
18
|
+
},
|
|
19
|
+
"1.3.33": {
|
|
20
|
+
"en": "Added live delta states for standard solar and Solar Extended control. Documented Solar Extended pool reference runtime update behavior.",
|
|
21
|
+
"de": "Live-Delta-Datenpunkte für Standard-Solar und Solar Extended ergänzt. Laufzeitverhalten der Solar-Extended-Poolreferenz dokumentiert.",
|
|
22
|
+
"ru": "Добавлены дельта-состояния в реальном времени для стандартного и расширенного управления солнечной энергией. Документированное поведение обновления во время выполнения ссылки на расширенный пул Solar.",
|
|
23
|
+
"pt": "Adicionados estados delta ao vivo para controle solar padrão e solar estendido. Comportamento de atualização de tempo de execução de referência do pool Solar Extended documentado.",
|
|
24
|
+
"nl": "Live deltatoestanden toegevoegd voor standaard zonne-energie en Solar Extended-regeling. Gedocumenteerd runtime-updategedrag van de Solar Extended-poolreferentie.",
|
|
25
|
+
"fr": "Ajout d'états delta en direct pour le contrôle solaire standard et solaire étendu. Comportement documenté de la mise à jour du runtime de référence du pool Solar Extended.",
|
|
26
|
+
"it": "Aggiunti stati delta live per il controllo solare standard e Solar Extended. Comportamento documentato dell'aggiornamento runtime di riferimento del pool Solar Extended.",
|
|
27
|
+
"es": "Se agregaron estados delta en vivo para control solar estándar y solar extendido. Comportamiento de actualización del tiempo de ejecución de referencia del grupo Solar Extended documentado.",
|
|
28
|
+
"pl": "Dodano stany delta na żywo dla standardowego sterowania solarnego i Solar Extended. Udokumentowane zachowanie aktualizacji środowiska uruchomieniowego referencyjnej puli Solar Extended.",
|
|
29
|
+
"uk": "Додано живі дельта-стани для стандартного сонячного та розширеного керування Solar. Задокументована поведінка оновлення середовища виконання довідкового пулу Solar Extended.",
|
|
30
|
+
"zh-cn": "为标准太阳能和太阳能扩展控制添加了实时增量状态。记录了 Solar Extended 池参考运行时更新行为。"
|
|
31
|
+
},
|
|
6
32
|
"1.3.32": {
|
|
7
33
|
"en": "Added circulation plausibility diagnostics. New checks detect implausible pump power, flow rates, and daily circulation jumps. Added detailed diagnostic states under circulation.plausibility to simplify troubleshooting and analysis of circulation calculations.",
|
|
8
34
|
"de": "Plausibilitätsdiagnose für die Umwälzberechnung hinzugefügt. Neue Prüfungen erkennen unplausible Pumpenleistungen, Durchflusswerte und Sprünge der Tagesumwälzung. Detaillierte Diagnose-States unter circulation.plausibility erleichtern die Fehlersuche und Analyse der Umwälzberechnung.",
|
|
@@ -41,32 +67,6 @@
|
|
|
41
67
|
"pl": "Zaktualizowano narzędzia wydania do wymaganej wersji minimalnej. Wyczyszczono nieaktualne klucze administratora i18n. Zastąpiono natywne liczniki czasu w pomocnikach pogodowych AI zegarami adaptera ioBroker.",
|
|
42
68
|
"uk": "Оновлено інструмент випуску до необхідної мінімальної версії. Очищено застарілі ключі адміністратора i18n. Вбудовані таймери в помічниках погоди штучного інтелекту замінено на таймери адаптера ioBroker.",
|
|
43
69
|
"zh-cn": "将发布工具更新为所需的最低版本。清理了过时的 Admin i18n 密钥。将 AI 天气助手中的本机计时器替换为 ioBroker 适配器计时器。"
|
|
44
|
-
},
|
|
45
|
-
"1.3.29": {
|
|
46
|
-
"en": "Added Pool Insights V1 with observations, status evaluation and runtime i18n support. Improved Pool Insights text generation and removed dependency on external summary blocks. Fixed missing initialization of season and solar warning runtime states from adapter configuration. Added admin UI notes explaining initial values versus runtime datapoint control.",
|
|
47
|
-
"de": "Pool Insights V1 mit Beobachtungen, Statusbewertung und Runtime-i18n hinzugefügt. Pool-Insights-Texte verbessert und die Abhängigkeit von externen Summary-Blöcken entfernt. Fehlende Initialisierung der Saison- und Solarwarnungs-States aus der Adapterkonfiguration behoben. Hinweise in der Admin-Oberfläche ergänzt, die den Unterschied zwischen Initialwerten und Runtime-Datenpunkten erläutern.",
|
|
48
|
-
"ru": "Добавлен Pool Insights V1 с наблюдениями, оценкой состояния и поддержкой i18n во время выполнения. Улучшено создание текста Pool Insights и удалена зависимость от внешних сводных блоков. Исправлена отсутствующая инициализация состояний выполнения сезонных и солнечных предупреждений из конфигурации адаптера. Добавлены примечания к интерфейсу администратора, объясняющие начальные значения и контроль точек данных во время выполнения.",
|
|
49
|
-
"pt": "Adicionado Pool Insights V1 com observações, avaliação de status e suporte ao tempo de execução i18n. Geração de texto do Pool Insights aprimorada e dependência removida de blocos de resumo externos. Corrigida a falta de inicialização dos estados de tempo de execução de aviso solar e de estação na configuração do adaptador. Adicionadas notas da UI administrativa explicando os valores iniciais versus controle de ponto de dados em tempo de execução.",
|
|
50
|
-
"nl": "Pool Insights V1 toegevoegd met observaties, statusevaluatie en runtime i18n-ondersteuning. Verbeterde tekstgeneratie in Pool Insights en verwijderde de afhankelijkheid van externe samenvattingsblokken. Probleem opgelost waarbij de initialisatie van seizoens- en zonne-waarschuwingsruntimestatussen uit de adapterconfiguratie ontbreekt. Aantekeningen in de beheerdersinterface toegevoegd waarin initiële waarden versus runtime-datapuntcontrole worden uitgelegd.",
|
|
51
|
-
"fr": "Ajout de Pool Insights V1 avec observations, évaluation de l'état et prise en charge du runtime i18n. Amélioration de la génération de texte Pool Insights et suppression de la dépendance aux blocs de résumé externes. Correction de l'initialisation manquante des états d'exécution de la saison et de l'avertissement solaire dans la configuration de l'adaptateur. Ajout de notes sur l'interface utilisateur d'administration expliquant les valeurs initiales par rapport au contrôle des points de données d'exécution.",
|
|
52
|
-
"it": "Aggiunto Pool Insights V1 con osservazioni, valutazione dello stato e supporto i18n runtime. Migliorata la generazione del testo di Pool Insights e rimossa la dipendenza dai blocchi di riepilogo esterni. Risolto il problema con l'inizializzazione mancante degli stati di runtime dell'avviso stagionale e solare dalla configurazione dell'adattatore. Aggiunte note sull'interfaccia utente di amministrazione che spiegano i valori iniziali rispetto al controllo del punto dati di runtime.",
|
|
53
|
-
"es": "Se agregó Pool Insights V1 con observaciones, evaluación de estado y soporte de tiempo de ejecución i18n. Se mejoró la generación de texto de Pool Insights y se eliminó la dependencia de bloques de resumen externos. Se corrigió la falta de inicialización de los estados de tiempo de ejecución de advertencia solar y de temporada desde la configuración del adaptador. Se agregaron notas de la interfaz de usuario del administrador que explican los valores iniciales versus el control de puntos de datos en tiempo de ejecución.",
|
|
54
|
-
"pl": "Dodano usługę Pool Insights V1 z obserwacjami, oceną stanu i obsługą środowiska wykonawczego i18n. Ulepszone generowanie tekstu Pool Insights i usunięta zależność od zewnętrznych bloków podsumowań. Naprawiono brakującą inicjalizację stanów wykonawczych sezonu i ostrzeżenia słonecznego z konfiguracji adaptera. Dodano uwagi do interfejsu administratora wyjaśniające wartości początkowe w porównaniu z kontrolą punktów danych w czasie wykonywania.",
|
|
55
|
-
"uk": "Додано Pool Insights V1 із спостереженнями, оцінкою стану та підтримкою i18n під час виконання. Покращено створення тексту Pool Insights і видалено залежність від зовнішніх блоків підсумків. Виправлено відсутність ініціалізації сезону та станів попередження про сонячне світло з конфігурації адаптера. Додано примітки інтерфейсу користувача адміністратора, що пояснюють початкові значення та керування точкою даних під час виконання.",
|
|
56
|
-
"zh-cn": "添加了 Pool Insights V1,具有观察、状态评估和运行时 i18n 支持。改进了 Pool Insights 文本生成并消除了对外部摘要块的依赖。修复了适配器配置中缺少季节和太阳警告运行时状态初始化的问题。添加了管理 UI 注释,解释初始值与运行时数据点控制。"
|
|
57
|
-
},
|
|
58
|
-
"1.3.28": {
|
|
59
|
-
"en": "Added configurable startup power check timeout for pump monitoring (5-10 seconds). Improved compatibility with delayed power measurements from smart plugs and power meters.",
|
|
60
|
-
"de": "Konfigurierbares Timeout für die Leistungsprüfung nach Pumpenstart (5-10 Sekunden) hinzugefügt. Kompatibilität mit verzögert aktualisierten Leistungswerten von Smart-Steckdosen und Leistungsmessern verbessert.",
|
|
61
|
-
"ru": "Добавлен настраиваемый тайм-аут проверки мощности при запуске для мониторинга насоса (5–10 секунд). Улучшена совместимость с измерениями мощности с задержкой от интеллектуальных розеток и измерителей мощности.",
|
|
62
|
-
"pt": "Adicionado tempo limite de verificação de energia de inicialização configurável para monitoramento da bomba (5 a 10 segundos). Compatibilidade aprimorada com medições de energia atrasadas de plugues inteligentes e medidores de energia.",
|
|
63
|
-
"nl": "Configureerbare time-out voor stroomcontrole bij opstarten toegevoegd voor pompbewaking (5-10 seconden). Verbeterde compatibiliteit met vertraagde stroommetingen van slimme stekkers en stroommeters.",
|
|
64
|
-
"fr": "Ajout d'un délai d'expiration configurable pour la vérification de l'alimentation au démarrage pour la surveillance de la pompe (5 à 10 secondes). Compatibilité améliorée avec les mesures de puissance retardées des prises intelligentes et des compteurs de puissance.",
|
|
65
|
-
"it": "Aggiunto timeout configurabile del controllo dell'alimentazione all'avvio per il monitoraggio della pompa (5-10 secondi). Compatibilità migliorata con misurazioni di potenza ritardate da prese intelligenti e misuratori di potenza.",
|
|
66
|
-
"es": "Se agregó un tiempo de espera de verificación de energía de inicio configurable para el monitoreo de la bomba (5-10 segundos). Compatibilidad mejorada con mediciones de potencia retrasadas de enchufes inteligentes y medidores de potencia.",
|
|
67
|
-
"pl": "Dodano konfigurowalny limit czasu sprawdzania mocy rozruchowej dla monitorowania pompy (5-10 sekund). Poprawiona kompatybilność z opóźnionymi pomiarami mocy z inteligentnych wtyczek i mierników mocy.",
|
|
68
|
-
"uk": "Додано настроюваний тайм-аут перевірки живлення при запуску для моніторингу насоса (5-10 секунд). Покращена сумісність із затримкою вимірювання потужності від розумних розеток і лічильників.",
|
|
69
|
-
"zh-cn": "添加了用于泵监控的可配置启动电源检查超时(5-10 秒)。改进了与智能插头和功率计的延迟功率测量的兼容性。"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"titleLang": {
|
|
@@ -2,8 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
const { I18n } = require('@iobroker/adapter-core');
|
|
4
4
|
|
|
5
|
-
const MAX_HISTORY_AGE_MS =
|
|
5
|
+
const MAX_HISTORY_AGE_MS = 7 * 24 * 60 * 60 * 1000;
|
|
6
|
+
const MAX_HISTORY_SAMPLES = 672;
|
|
7
|
+
const MAX_HISTORY_BYTES = 64 * 1024;
|
|
6
8
|
const MIN_SAMPLE_INTERVAL_MS = 15 * 60 * 1000;
|
|
9
|
+
const MAX_DAILY_HISTORY_AGE_MS = 32 * 24 * 60 * 60 * 1000;
|
|
10
|
+
const MAX_DAILY_HISTORY_SAMPLES = 32;
|
|
11
|
+
const MAX_DAILY_HISTORY_BYTES = 8 * 1024;
|
|
12
|
+
const DAILY_REFERENCE_TOLERANCE_MS = 4 * 24 * 60 * 60 * 1000;
|
|
7
13
|
|
|
8
14
|
const chemistryOrpHelper = {
|
|
9
15
|
adapter: null,
|
|
@@ -167,7 +173,7 @@ const chemistryOrpHelper = {
|
|
|
167
173
|
return;
|
|
168
174
|
}
|
|
169
175
|
|
|
170
|
-
await this._processValue(source, rawValue, reason,
|
|
176
|
+
await this._processValue(source, rawValue, reason, source === 'manual');
|
|
171
177
|
},
|
|
172
178
|
|
|
173
179
|
_scheduleEvaluation(reason, delayMs = 500) {
|
|
@@ -292,7 +298,7 @@ const chemistryOrpHelper = {
|
|
|
292
298
|
await this._updateLastValues(value, now);
|
|
293
299
|
|
|
294
300
|
const history = await this._updateHistory(value, now, forceSample);
|
|
295
|
-
const trend = await this._calculateTrend(value, now, history);
|
|
301
|
+
const trend = await this._calculateTrend(value, now, history.samples, history.dailySamples);
|
|
296
302
|
const phReference = await this._updatePhReference();
|
|
297
303
|
const evaluation = await this._evaluateOrp(value, phReference);
|
|
298
304
|
|
|
@@ -406,16 +412,17 @@ const chemistryOrpHelper = {
|
|
|
406
412
|
|
|
407
413
|
async _updateHistory(value, now, forceSample) {
|
|
408
414
|
let samples = await this._readJsonArray('chemistry.orp.history.samples_json');
|
|
415
|
+
const dailySeedSamples = samples;
|
|
409
416
|
const nowTs = now.getTime();
|
|
410
417
|
const minTs = nowTs - MAX_HISTORY_AGE_MS;
|
|
411
418
|
|
|
412
|
-
samples = samples.filter(
|
|
413
|
-
sample => sample && Number(sample.ts) >= minTs && Number.isFinite(Number(sample.value)),
|
|
414
|
-
);
|
|
419
|
+
samples = samples.filter(sample => sample.ts >= minTs && sample.ts <= nowTs);
|
|
415
420
|
|
|
416
421
|
const newest = samples.length ? samples[samples.length - 1] : null;
|
|
417
422
|
const newestTs = newest ? Number(newest.ts) : 0;
|
|
418
|
-
const
|
|
423
|
+
const withinSampleInterval = newest && nowTs - newestTs < MIN_SAMPLE_INTERVAL_MS;
|
|
424
|
+
const sameValue = newest && Number(newest.value) === value;
|
|
425
|
+
const shouldStore = !newest || !withinSampleInterval || (forceSample && !sameValue);
|
|
419
426
|
|
|
420
427
|
if (shouldStore) {
|
|
421
428
|
samples.push({
|
|
@@ -425,9 +432,12 @@ const chemistryOrpHelper = {
|
|
|
425
432
|
});
|
|
426
433
|
}
|
|
427
434
|
|
|
428
|
-
samples = samples.filter(sample => sample &&
|
|
435
|
+
samples = samples.filter(sample => sample.ts >= minTs && sample.ts <= nowTs).slice(-MAX_HISTORY_SAMPLES);
|
|
436
|
+
|
|
437
|
+
const preparedHistory = this._prepareHistoryForWrite(samples, 'chemistry.orp.history.samples_json');
|
|
438
|
+
samples = preparedHistory.samples;
|
|
429
439
|
|
|
430
|
-
await this._setString('chemistry.orp.history.samples_json',
|
|
440
|
+
await this._setString('chemistry.orp.history.samples_json', preparedHistory.json);
|
|
431
441
|
await this._setNumber('chemistry.orp.history.samples_count', samples.length);
|
|
432
442
|
|
|
433
443
|
if (samples.length) {
|
|
@@ -443,15 +453,20 @@ const chemistryOrpHelper = {
|
|
|
443
453
|
);
|
|
444
454
|
}
|
|
445
455
|
|
|
446
|
-
|
|
456
|
+
const dailySamples = await this._updateDailyHistory(dailySeedSamples, value, now, shouldStore);
|
|
457
|
+
return { samples, dailySamples };
|
|
447
458
|
},
|
|
448
459
|
|
|
449
|
-
async _calculateTrend(currentValue, now, samples) {
|
|
460
|
+
async _calculateTrend(currentValue, now, samples, dailySamples) {
|
|
450
461
|
const nowTs = now.getTime();
|
|
451
462
|
|
|
452
463
|
const ref24h = this._findReferenceSample(samples, nowTs - 24 * 60 * 60 * 1000);
|
|
453
464
|
const ref7d = this._findReferenceSample(samples, nowTs - 7 * 24 * 60 * 60 * 1000);
|
|
454
|
-
const ref30d = this._findReferenceSample(
|
|
465
|
+
const ref30d = this._findReferenceSample(
|
|
466
|
+
dailySamples,
|
|
467
|
+
nowTs - 30 * 24 * 60 * 60 * 1000,
|
|
468
|
+
DAILY_REFERENCE_TOLERANCE_MS,
|
|
469
|
+
);
|
|
455
470
|
|
|
456
471
|
const delta24h = ref24h ? currentValue - ref24h.value : 0;
|
|
457
472
|
const delta7d = ref7d ? currentValue - ref7d.value : 0;
|
|
@@ -472,7 +487,7 @@ const chemistryOrpHelper = {
|
|
|
472
487
|
};
|
|
473
488
|
},
|
|
474
489
|
|
|
475
|
-
_findReferenceSample(samples, targetTs) {
|
|
490
|
+
_findReferenceSample(samples, targetTs, toleranceMs = null) {
|
|
476
491
|
if (!samples.length) {
|
|
477
492
|
return null;
|
|
478
493
|
}
|
|
@@ -482,7 +497,7 @@ const chemistryOrpHelper = {
|
|
|
482
497
|
|
|
483
498
|
for (const sample of samples) {
|
|
484
499
|
const ts = Number(sample.ts);
|
|
485
|
-
const value = Number(sample.value);
|
|
500
|
+
const value = Number(sample.value ?? sample.last);
|
|
486
501
|
|
|
487
502
|
if (!Number.isFinite(ts) || !Number.isFinite(value)) {
|
|
488
503
|
continue;
|
|
@@ -500,7 +515,7 @@ const chemistryOrpHelper = {
|
|
|
500
515
|
}
|
|
501
516
|
}
|
|
502
517
|
|
|
503
|
-
return best;
|
|
518
|
+
return toleranceMs === null || bestDistance <= toleranceMs ? best : null;
|
|
504
519
|
},
|
|
505
520
|
|
|
506
521
|
_getOverallDirection(ref24h, ref7d, ref30d, delta24h, delta7d, delta30d) {
|
|
@@ -748,17 +763,264 @@ const chemistryOrpHelper = {
|
|
|
748
763
|
return !!state?.val;
|
|
749
764
|
},
|
|
750
765
|
|
|
766
|
+
async _updateDailyHistory(shortTermSamples, value, now, sampleStored) {
|
|
767
|
+
const id = 'chemistry.orp.history.daily_json';
|
|
768
|
+
let dailySamples = await this._readDailyJson(id);
|
|
769
|
+
const nowTs = now.getTime();
|
|
770
|
+
const minTs = nowTs - MAX_DAILY_HISTORY_AGE_MS;
|
|
771
|
+
const initializeFromSeeds = dailySamples.length === 0;
|
|
772
|
+
|
|
773
|
+
if (initializeFromSeeds) {
|
|
774
|
+
const legacyValueState = await this.adapter.getStateAsync('chemistry.orp.trend.reference_30d_value');
|
|
775
|
+
const legacyAtState = await this.adapter.getStateAsync('chemistry.orp.trend.reference_30d_at');
|
|
776
|
+
const legacyValue = Number(legacyValueState?.val);
|
|
777
|
+
const legacyTs = Number(legacyAtState?.val);
|
|
778
|
+
|
|
779
|
+
if (
|
|
780
|
+
Number.isFinite(legacyValue) &&
|
|
781
|
+
legacyValue >= 0 &&
|
|
782
|
+
legacyValue <= 1200 &&
|
|
783
|
+
Number.isFinite(legacyTs) &&
|
|
784
|
+
legacyTs >= minTs &&
|
|
785
|
+
legacyTs <= nowTs &&
|
|
786
|
+
!shortTermSamples.some(sample => this._dayKey(sample.ts) === this._dayKey(legacyTs))
|
|
787
|
+
) {
|
|
788
|
+
this._addDailySample(dailySamples, legacyTs, legacyValue);
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
for (const sample of shortTermSamples) {
|
|
792
|
+
if (sample.ts >= minTs && sample.ts <= nowTs) {
|
|
793
|
+
this._addDailySample(dailySamples, sample.ts, sample.value);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
if (sampleStored) {
|
|
797
|
+
this._addDailySample(dailySamples, nowTs, value);
|
|
798
|
+
}
|
|
799
|
+
} else if (sampleStored) {
|
|
800
|
+
this._addDailySample(dailySamples, nowTs, value);
|
|
801
|
+
}
|
|
802
|
+
|
|
803
|
+
dailySamples = dailySamples
|
|
804
|
+
.filter(sample => sample.ts >= minTs && sample.ts <= nowTs)
|
|
805
|
+
.sort((a, b) => a.ts - b.ts)
|
|
806
|
+
.slice(-MAX_DAILY_HISTORY_SAMPLES);
|
|
807
|
+
|
|
808
|
+
const preparedHistory = this._prepareDailyHistoryForWrite(dailySamples, id);
|
|
809
|
+
await this._setString(id, preparedHistory.json);
|
|
810
|
+
return preparedHistory.samples;
|
|
811
|
+
},
|
|
812
|
+
|
|
813
|
+
_addDailySample(dailySamples, sampleTs, value) {
|
|
814
|
+
const day = this._dayKey(sampleTs);
|
|
815
|
+
const existing = dailySamples.find(sample => sample.day === day);
|
|
816
|
+
|
|
817
|
+
if (!existing) {
|
|
818
|
+
dailySamples.push({
|
|
819
|
+
day,
|
|
820
|
+
ts: this._dayStartTs(sampleTs),
|
|
821
|
+
min: value,
|
|
822
|
+
max: value,
|
|
823
|
+
avg: value,
|
|
824
|
+
last: value,
|
|
825
|
+
count: 1,
|
|
826
|
+
});
|
|
827
|
+
return;
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
existing.min = Math.min(existing.min, value);
|
|
831
|
+
existing.max = Math.max(existing.max, value);
|
|
832
|
+
existing.avg = (existing.avg * existing.count + value) / (existing.count + 1);
|
|
833
|
+
existing.last = value;
|
|
834
|
+
existing.count += 1;
|
|
835
|
+
},
|
|
836
|
+
|
|
837
|
+
async _readDailyJson(id) {
|
|
838
|
+
const state = await this.adapter.getStateAsync(id);
|
|
839
|
+
const rawValue = state?.val;
|
|
840
|
+
|
|
841
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
842
|
+
return [];
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
if (typeof rawValue !== 'string') {
|
|
846
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Ignoring non-string daily history state ${id}.`);
|
|
847
|
+
return [];
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
if (Buffer.byteLength(rawValue, 'utf8') > MAX_DAILY_HISTORY_BYTES) {
|
|
851
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Ignoring oversized daily history state ${id} (> 8 KB).`);
|
|
852
|
+
return [];
|
|
853
|
+
}
|
|
854
|
+
|
|
855
|
+
try {
|
|
856
|
+
const parsed = JSON.parse(rawValue);
|
|
857
|
+
if (!Array.isArray(parsed)) {
|
|
858
|
+
return [];
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const nowTs = Date.now();
|
|
862
|
+
const normalized = parsed
|
|
863
|
+
.map(sample => {
|
|
864
|
+
const sourceTs = Number(sample?.ts);
|
|
865
|
+
const legacyValue = Number(sample?.value);
|
|
866
|
+
const min = Number(sample?.min ?? legacyValue);
|
|
867
|
+
const max = Number(sample?.max ?? legacyValue);
|
|
868
|
+
const avg = Number(sample?.avg ?? legacyValue);
|
|
869
|
+
const last = Number(sample?.last ?? legacyValue);
|
|
870
|
+
const count = sample?.count === undefined ? 1 : Number(sample.count);
|
|
871
|
+
|
|
872
|
+
if (!Number.isFinite(sourceTs) || sourceTs <= 0 || sourceTs > nowTs) {
|
|
873
|
+
return null;
|
|
874
|
+
}
|
|
875
|
+
if (
|
|
876
|
+
!Number.isFinite(min) ||
|
|
877
|
+
!Number.isFinite(max) ||
|
|
878
|
+
!Number.isFinite(avg) ||
|
|
879
|
+
!Number.isFinite(last) ||
|
|
880
|
+
min < 0 ||
|
|
881
|
+
max > 1200 ||
|
|
882
|
+
min > max ||
|
|
883
|
+
avg < min ||
|
|
884
|
+
avg > max ||
|
|
885
|
+
last < min ||
|
|
886
|
+
last > max
|
|
887
|
+
) {
|
|
888
|
+
return null;
|
|
889
|
+
}
|
|
890
|
+
if (!Number.isInteger(count) || count < 1 || count > 1000000) {
|
|
891
|
+
return null;
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
return {
|
|
895
|
+
day: this._dayKey(sourceTs),
|
|
896
|
+
ts: this._dayStartTs(sourceTs),
|
|
897
|
+
min,
|
|
898
|
+
max,
|
|
899
|
+
avg,
|
|
900
|
+
last,
|
|
901
|
+
count,
|
|
902
|
+
};
|
|
903
|
+
})
|
|
904
|
+
.filter(Boolean)
|
|
905
|
+
.sort((a, b) => a.ts - b.ts);
|
|
906
|
+
|
|
907
|
+
const byDay = new Map();
|
|
908
|
+
for (const sample of normalized) {
|
|
909
|
+
if (!byDay.has(sample.day)) {
|
|
910
|
+
byDay.set(sample.day, sample);
|
|
911
|
+
}
|
|
912
|
+
}
|
|
913
|
+
return [...byDay.values()].slice(-MAX_DAILY_HISTORY_SAMPLES);
|
|
914
|
+
} catch {
|
|
915
|
+
return [];
|
|
916
|
+
}
|
|
917
|
+
},
|
|
918
|
+
|
|
919
|
+
_prepareDailyHistoryForWrite(samples, id) {
|
|
920
|
+
let limitedSamples = samples.slice(-MAX_DAILY_HISTORY_SAMPLES);
|
|
921
|
+
let json = JSON.stringify(limitedSamples);
|
|
922
|
+
let trimmedForSize = false;
|
|
923
|
+
|
|
924
|
+
while (limitedSamples.length && Buffer.byteLength(json, 'utf8') > MAX_DAILY_HISTORY_BYTES) {
|
|
925
|
+
limitedSamples = limitedSamples.slice(1);
|
|
926
|
+
json = JSON.stringify(limitedSamples);
|
|
927
|
+
trimmedForSize = true;
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
if (trimmedForSize) {
|
|
931
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Daily history ${id} was trimmed to stay within 8 KB.`);
|
|
932
|
+
}
|
|
933
|
+
|
|
934
|
+
if (Buffer.byteLength(json, 'utf8') > MAX_DAILY_HISTORY_BYTES) {
|
|
935
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Daily history ${id} could not be limited; resetting it.`);
|
|
936
|
+
return { samples: [], json: '[]' };
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
return { samples: limitedSamples, json };
|
|
940
|
+
},
|
|
941
|
+
|
|
942
|
+
_dayKey(ts) {
|
|
943
|
+
const date = new Date(ts);
|
|
944
|
+
const year = String(date.getFullYear());
|
|
945
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
946
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
947
|
+
return [year, month, day].join('-');
|
|
948
|
+
},
|
|
949
|
+
|
|
950
|
+
_dayStartTs(ts) {
|
|
951
|
+
const date = new Date(ts);
|
|
952
|
+
date.setHours(0, 0, 0, 0);
|
|
953
|
+
return date.getTime();
|
|
954
|
+
},
|
|
955
|
+
|
|
751
956
|
async _readJsonArray(id) {
|
|
752
957
|
const state = await this.adapter.getStateAsync(id);
|
|
958
|
+
const rawValue = state?.val;
|
|
959
|
+
|
|
960
|
+
if (rawValue === null || rawValue === undefined || rawValue === '') {
|
|
961
|
+
return [];
|
|
962
|
+
}
|
|
963
|
+
|
|
964
|
+
if (typeof rawValue !== 'string') {
|
|
965
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Ignoring non-string history state ${id}.`);
|
|
966
|
+
return [];
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
if (Buffer.byteLength(rawValue, 'utf8') > MAX_HISTORY_BYTES) {
|
|
970
|
+
this.adapter.log.warn(`[chemistryOrpHelper] Ignoring oversized history state ${id} (> 64 KB).`);
|
|
971
|
+
return [];
|
|
972
|
+
}
|
|
753
973
|
|
|
754
974
|
try {
|
|
755
|
-
const parsed = JSON.parse(
|
|
756
|
-
|
|
975
|
+
const parsed = JSON.parse(rawValue);
|
|
976
|
+
if (!Array.isArray(parsed)) {
|
|
977
|
+
return [];
|
|
978
|
+
}
|
|
979
|
+
|
|
980
|
+
const nowTs = Date.now();
|
|
981
|
+
return parsed
|
|
982
|
+
.map(sample => {
|
|
983
|
+
const ts = Number(sample?.ts);
|
|
984
|
+
const value = Number(sample?.value);
|
|
985
|
+
if (!Number.isFinite(ts) || ts <= 0 || ts > nowTs) {
|
|
986
|
+
return null;
|
|
987
|
+
}
|
|
988
|
+
if (!Number.isFinite(value) || value < 0 || value > 1200) {
|
|
989
|
+
return null;
|
|
990
|
+
}
|
|
991
|
+
return { ts, time: this._formatDateTime(new Date(ts)), value };
|
|
992
|
+
})
|
|
993
|
+
.filter(Boolean)
|
|
994
|
+
.sort((a, b) => a.ts - b.ts)
|
|
995
|
+
.slice(-MAX_HISTORY_SAMPLES);
|
|
757
996
|
} catch {
|
|
758
997
|
return [];
|
|
759
998
|
}
|
|
760
999
|
},
|
|
761
1000
|
|
|
1001
|
+
_prepareHistoryForWrite(samples, id) {
|
|
1002
|
+
let limitedSamples = samples.slice(-MAX_HISTORY_SAMPLES);
|
|
1003
|
+
let json = JSON.stringify(limitedSamples);
|
|
1004
|
+
let trimmedForSize = false;
|
|
1005
|
+
|
|
1006
|
+
while (limitedSamples.length && Buffer.byteLength(json, 'utf8') > MAX_HISTORY_BYTES) {
|
|
1007
|
+
limitedSamples = limitedSamples.slice(1);
|
|
1008
|
+
json = JSON.stringify(limitedSamples);
|
|
1009
|
+
trimmedForSize = true;
|
|
1010
|
+
}
|
|
1011
|
+
|
|
1012
|
+
if (trimmedForSize) {
|
|
1013
|
+
this.adapter.log.warn(`[chemistryOrpHelper] History ${id} was trimmed to stay within 64 KB.`);
|
|
1014
|
+
}
|
|
1015
|
+
|
|
1016
|
+
if (Buffer.byteLength(json, 'utf8') > MAX_HISTORY_BYTES) {
|
|
1017
|
+
this.adapter.log.warn(`[chemistryOrpHelper] History ${id} could not be limited safely; resetting it.`);
|
|
1018
|
+
return { samples: [], json: '[]' };
|
|
1019
|
+
}
|
|
1020
|
+
|
|
1021
|
+
return { samples: limitedSamples, json };
|
|
1022
|
+
},
|
|
1023
|
+
|
|
762
1024
|
async _setString(id, value) {
|
|
763
1025
|
await this.adapter.setStateChangedAsync(id, { val: String(value ?? ''), ack: true });
|
|
764
1026
|
},
|