iobroker.poolcontrol 1.3.19 → 1.3.21
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 +35 -46
- package/io-package.json +27 -27
- package/lib/helpers/actuatorsHelper.js +176 -1
- package/lib/stateDefinitions/actuatorsStates.js +192 -0
- package/lib/stateDefinitions/timeStates.js +26 -16
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -188,6 +188,41 @@ New features are added regularly – please refer to the changelog.
|
|
|
188
188
|
---
|
|
189
189
|
|
|
190
190
|
## Changelog
|
|
191
|
+
### 1.3.21 (2026-05-17)
|
|
192
|
+
|
|
193
|
+
NEW: Follow-pump devices
|
|
194
|
+
|
|
195
|
+
Added a new `actuators.follow_pump_devices` area.
|
|
196
|
+
|
|
197
|
+
Up to three external devices can now automatically follow the operation of the main pump.
|
|
198
|
+
|
|
199
|
+
Typical examples:
|
|
200
|
+
|
|
201
|
+
- UV systems
|
|
202
|
+
- Water features
|
|
203
|
+
- Auxiliary filters
|
|
204
|
+
- Additional circulation devices
|
|
205
|
+
|
|
206
|
+
Features:
|
|
207
|
+
|
|
208
|
+
- Automatic ON when the main pump starts
|
|
209
|
+
- Automatic OFF when the main pump stops
|
|
210
|
+
- Configurable target state per device
|
|
211
|
+
- Validation of target states:
|
|
212
|
+
- state exists
|
|
213
|
+
- boolean type required
|
|
214
|
+
- writable required
|
|
215
|
+
- Protection against invalid internal follow-pump targets
|
|
216
|
+
- Persistent configuration values
|
|
217
|
+
|
|
218
|
+
### 1.3.20 (2026-05-13)
|
|
219
|
+
|
|
220
|
+
- Added runtime self-healing for missed pump start events.
|
|
221
|
+
- Stabilized runtime and circulation calculations for delayed or filtered pump switch updates.
|
|
222
|
+
- Fixed a rare synchronization issue where circulation counting could stop although pump live values were still available.
|
|
223
|
+
- Protected time control states from being overwritten during adapter updates.
|
|
224
|
+
- Preserved configured time windows, start/end times and weekdays during adapter reinstallations or updates.
|
|
225
|
+
|
|
191
226
|
### 1.3.19 (2026-05-13)
|
|
192
227
|
|
|
193
228
|
- Added runtime self-healing for missed pump start events
|
|
@@ -233,52 +268,6 @@ New features are added regularly – please refer to the changelog.
|
|
|
233
268
|
- extended pH helper with history, trend and summary logic
|
|
234
269
|
- existing pH input, evaluation and mix-run logic remain backward compatible
|
|
235
270
|
|
|
236
|
-
### 1.3.16 (2026-05-11)
|
|
237
|
-
|
|
238
|
-
- Fixed circulation calculation in time mode when live flow values were not recalculated after helper-driven pump starts.
|
|
239
|
-
- Improved speech system stability.
|
|
240
|
-
- Stabilized runtime persistence.
|
|
241
|
-
- Reduced repeated solar notifications.
|
|
242
|
-
- Added internal stability improvements.
|
|
243
|
-
|
|
244
|
-
- Added new ORP/Redox chemistry preparation:
|
|
245
|
-
- new `chemistryOrpStates.js`
|
|
246
|
-
- new `chemistryOrpHelper.js`
|
|
247
|
-
- integrated ORP handling into `main.js`
|
|
248
|
-
- supports disabled/manual/state input modes
|
|
249
|
-
- ORP value handling in mV
|
|
250
|
-
- pH reference from `chemistry.ph.enabled` and `chemistry.ph.input.current_value`
|
|
251
|
-
- measurement location, pump and stabilization logic aligned with pH/TDS
|
|
252
|
-
- ORP evaluation without automatic dosing or chlorine control
|
|
253
|
-
- ORP 24h/7d/30d trend support
|
|
254
|
-
- ORP history support
|
|
255
|
-
- ORP text/HTML/JSON outputs
|
|
256
|
-
|
|
257
|
-
- Added ORP i18n texts.
|
|
258
|
-
- Aligned pH structure with TDS/ORP:
|
|
259
|
-
- added `chemistry.ph.history.*`
|
|
260
|
-
- added `chemistry.ph.trend.*`
|
|
261
|
-
- added `chemistry.ph.outputs.*`
|
|
262
|
-
- extended pH helper with history, trend and summary logic
|
|
263
|
-
- existing pH input, evaluation and mix-run logic remain backward compatible
|
|
264
|
-
|
|
265
|
-
### 1.3.15 (2026-05-08)
|
|
266
|
-
|
|
267
|
-
- Added speech source cooldown handling for solar and time announcements
|
|
268
|
-
- Added configurable `speech.sources.*` states for source-based speech control
|
|
269
|
-
- Reduced excessive solar speech notifications during unstable weather conditions
|
|
270
|
-
- Added persistent numeric runtime second states:
|
|
271
|
-
- `runtime.total_seconds`
|
|
272
|
-
- `runtime.today_seconds`
|
|
273
|
-
- `runtime.current_session_seconds`
|
|
274
|
-
- `runtime.season_total_seconds`
|
|
275
|
-
- Improved runtime restore handling using numeric fallback logic
|
|
276
|
-
- Fixed live runtime display consistency for total, today and season runtimes
|
|
277
|
-
- Improved season runtime handling using `status.season_active`
|
|
278
|
-
- Converted runtime helper timers to ioBroker adapter timer methods
|
|
279
|
-
- Improved runtime timer cleanup and reset protection
|
|
280
|
-
- Improved speech logging for skipped and cooldown-limited announcements
|
|
281
|
-
|
|
282
271
|
## Support
|
|
283
272
|
- [ioBroker Forum](https://forum.iobroker.net/)
|
|
284
273
|
- [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.21",
|
|
5
5
|
"news": {
|
|
6
|
+
"1.3.21": {
|
|
7
|
+
"en": "Added new follow-pump devices. Up to three external devices can now automatically follow the main pump operation. Added validation for target states including existence, boolean type and writable checks. Added protection against internal follow-pump loops.",
|
|
8
|
+
"de": "Neue Geräte mit Pumpenlauf hinzugefügt. Bis zu drei externe Geräte können jetzt automatisch dem Lauf der Hauptpumpe folgen. Zusätzlich wurden Prüfungen für Ziel-Datenpunkte (Existenz, Boolean-Typ und Schreibbarkeit) ergänzt. Schutz gegen interne Follow-Pump-Schleifen hinzugefügt.",
|
|
9
|
+
"ru": "Добавлены новые устройства, следующие за работой насоса. До трёх внешних устройств теперь могут автоматически следовать за работой основного насоса. Добавлена проверка целевых состояний (существование, тип boolean и возможность записи). Добавлена защита от внутренних циклов Follow-Pump.",
|
|
10
|
+
"pt": "Adicionados novos dispositivos dependentes da bomba. Até três dispositivos externos podem agora seguir automaticamente o funcionamento da bomba principal. Adicionadas verificações para estados alvo (existência, tipo booleano e permissão de escrita). Adicionada proteção contra loops internos Follow-Pump.",
|
|
11
|
+
"nl": "Nieuwe apparaten met pompkoppeling toegevoegd. Tot drie externe apparaten kunnen nu automatisch de hoofdpomp volgen. Validatie voor doelstatussen toegevoegd inclusief bestaan, boolean-type en schrijfbaarheid. Bescherming tegen interne Follow-Pump-lussen toegevoegd.",
|
|
12
|
+
"fr": "Ajout de nouveaux appareils liés à la pompe. Jusqu'à trois appareils externes peuvent désormais suivre automatiquement le fonctionnement de la pompe principale. Validation des états cibles ajoutée (existence, type booléen et écriture). Protection contre les boucles internes Follow-Pump ajoutée.",
|
|
13
|
+
"it": "Aggiunti nuovi dispositivi collegati alla pompa. Fino a tre dispositivi esterni possono ora seguire automaticamente il funzionamento della pompa principale. Aggiunti controlli per gli stati di destinazione (esistenza, tipo booleano e scrivibilità). Aggiunta protezione contro i loop interni Follow-Pump.",
|
|
14
|
+
"es": "Añadidos nuevos dispositivos dependientes de la bomba. Hasta tres dispositivos externos ahora pueden seguir automáticamente el funcionamiento de la bomba principal. Se añadieron validaciones para estados destino (existencia, tipo booleano y escritura). Añadida protección contra bucles internos Follow-Pump.",
|
|
15
|
+
"pl": "Dodano nowe urządzenia podążające za pompą. Do trzech urządzeń zewnętrznych może teraz automatycznie podążać za pracą głównej pompy. Dodano sprawdzanie stanów docelowych (istnienie, typ boolean i możliwość zapisu). Dodano ochronę przed wewnętrznymi pętlami Follow-Pump.",
|
|
16
|
+
"uk": "Додано нові пристрої, що слідують за насосом. До трьох зовнішніх пристроїв тепер можуть автоматично працювати разом з головним насосом. Додано перевірку цільових станів (існування, тип boolean та можливість запису). Додано захист від внутрішніх циклів Follow-Pump.",
|
|
17
|
+
"zh-cn": "新增随泵运行设备。现在最多可让三个外部设备自动跟随主泵运行。增加了目标状态验证(存在性、布尔类型和可写性)。增加了内部 Follow-Pump 循环保护。"
|
|
18
|
+
},
|
|
19
|
+
"1.3.20": {
|
|
20
|
+
"en": "Added runtime self-healing for missed pump start events and protected time control states from being overwritten during adapter updates.",
|
|
21
|
+
"de": "Runtime-Selbstheilung für verpasste Pumpenstart-Ereignisse ergänzt und Zeitsteuerungs-Datenpunkte vor dem Überschreiben bei Adapter-Updates geschützt.",
|
|
22
|
+
"ru": "Добавлено самовосстановление во время выполнения для пропущенных событий запуска насоса и защищены состояния контроля времени от перезаписи во время обновлений адаптера.",
|
|
23
|
+
"pt": "Adicionada autocorreção em tempo de execução para eventos de inicialização de bomba perdidos e estados de controle de tempo protegidos contra substituição durante atualizações do adaptador.",
|
|
24
|
+
"nl": "Zelfherstel tijdens runtime toegevoegd voor gemiste pompstartgebeurtenissen en beschermde tijdcontrolestatussen tegen overschrijving tijdens adapterupdates.",
|
|
25
|
+
"fr": "Ajout de l'auto-réparation au moment de l'exécution pour les événements de démarrage de pompe manqués et des états de contrôle du temps protégés contre l'écrasement lors des mises à jour de l'adaptateur.",
|
|
26
|
+
"it": "Aggiunta l'autoriparazione del runtime per gli eventi di mancato avvio della pompa e gli stati di controllo temporale protetti dalla sovrascrittura durante gli aggiornamenti dell'adattatore.",
|
|
27
|
+
"es": "Se agregó autorreparación en tiempo de ejecución para eventos de inicio de bomba perdidos y estados de control de tiempo protegidos para que no se sobrescriban durante las actualizaciones del adaptador.",
|
|
28
|
+
"pl": "Dodano funkcję samonaprawy w czasie wykonywania w przypadku nieodebranych zdarzeń uruchomienia pompy i zabezpieczono stany kontroli czasu przed nadpisaniem podczas aktualizacji adaptera.",
|
|
29
|
+
"uk": "Додано самовідновлення під час виконання для пропущених подій запуску насоса та захищено стани керування часом від перезапису під час оновлення адаптера.",
|
|
30
|
+
"zh-cn": "添加了针对错过的泵启动事件的运行时自我修复,并保护时间控制状态在适配器更新期间不被覆盖。"
|
|
31
|
+
},
|
|
6
32
|
"1.3.19": {
|
|
7
33
|
"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
34
|
"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.",
|
|
@@ -41,32 +67,6 @@
|
|
|
41
67
|
"pl": "Naprawiono problem z pakowaniem wersji od wersji 1.3.16. Poprawiona stabilność systemu mowy, ustabilizowana trwałość czasu działania, zmniejszona liczba powtarzających się powiadomień słonecznych, stałe obliczanie cyrkulacji w trybie czasowym i przygotowany nowy obszar analizy chemicznej ORP/Redox.",
|
|
42
68
|
"uk": "Виправлена проблема упаковки випуску з версії 1.3.16. Покращена стабільність мовної системи, стабілізована стійкість під час виконання, зменшено кількість повторюваних сонячних сповіщень, виправлено обчислення циркуляції в часовому режимі та підготовлено нову область аналізу ORP/Redox.",
|
|
43
69
|
"zh-cn": "修复了 v1.3.16 版本的打包问题。提高了语音系统的稳定性,稳定了运行时间的持久性,减少了重复的太阳通知,修复了时间模式下的循环计算,并准备了新的ORP/氧化还原化学分析区域。"
|
|
44
|
-
},
|
|
45
|
-
"1.3.16": {
|
|
46
|
-
"en": "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.",
|
|
47
|
-
"de": "Sprachsystem verbessert, Runtime-Persistenz stabilisiert, wiederholte Solarbenachrichtigungen reduziert, Umwälzberechnung im Zeitmodus korrigiert und neuer ORP-/Redox-Chemiebereich vorbereitet.",
|
|
48
|
-
"ru": "Улучшена стабильность речевой системы, стабилизировано постоянство времени выполнения, уменьшено количество повторных уведомлений о солнечной радиации, исправлен расчет циркуляции во временном режиме и подготовлена новая область химического анализа ОВП/окислительно-восстановительного потенциала.",
|
|
49
|
-
"pt": "Melhor estabilidade do sistema de fala, persistência de tempo de execução estabilizada, redução de notificações solares repetidas, cálculo de circulação fixo no modo de tempo e nova área de análise química ORP/Redox preparada.",
|
|
50
|
-
"nl": "Verbeterde stabiliteit van het spraaksysteem, gestabiliseerde runtime-persistentie, minder herhaalde zonnemeldingen, vaste circulatieberekening in tijdmodus en voorbereid voor een nieuw ORP/Redox-chemieanalysegebied.",
|
|
51
|
-
"fr": "Amélioration de la stabilité du système vocal, persistance d'exécution stabilisée, réduction des notifications solaires répétées, calcul de la circulation fixe en mode temps et préparation d'une nouvelle zone d'analyse chimique ORP/Redox.",
|
|
52
|
-
"it": "Stabilità del sistema vocale migliorata, persistenza del tempo di esecuzione stabilizzata, notifiche solari ripetute ridotte, calcolo della circolazione fissa in modalità temporale e nuova area di analisi chimica ORP/Redox preparata.",
|
|
53
|
-
"es": "Se mejoró la estabilidad del sistema de voz, se estabilizó la persistencia del tiempo de ejecución, se redujeron las notificaciones solares repetidas, se arregló el cálculo de la circulación en el modo de tiempo y se preparó una nueva área de análisis de química ORP/Redox.",
|
|
54
|
-
"pl": "Poprawiona stabilność systemu mowy, ustabilizowana trwałość czasu działania, zmniejszona liczba powtarzających się powiadomień słonecznych, stałe obliczanie cyrkulacji w trybie czasowym i przygotowany nowy obszar analizy chemicznej ORP/Redox.",
|
|
55
|
-
"uk": "Покращена стабільність мовної системи, стабілізована стійкість під час виконання, зменшено кількість повторюваних сонячних сповіщень, виправлено обчислення циркуляції в часовому режимі та підготовлено нову область аналізу ORP/Redox.",
|
|
56
|
-
"zh-cn": "提高了语音系统的稳定性,稳定了运行时间的持久性,减少了重复的太阳通知,修复了时间模式下的循环计算,并准备了新的ORP/氧化还原化学分析区域。"
|
|
57
|
-
},
|
|
58
|
-
"1.3.15": {
|
|
59
|
-
"en": "Added speech source cooldown handling, improved solar/time speech control, added persistent runtime second states, improved runtime restore logic and converted runtime timers to ioBroker adapter timers.",
|
|
60
|
-
"de": "Sprachquellen mit Cooldown-Steuerung ergänzt, Solar-/Zeit-Sprachsteuerung verbessert, persistente Runtime-Sekundenstates hinzugefügt, Runtime-Wiederherstellung verbessert und Runtime-Timer auf ioBroker-Adapter-Timer umgestellt.",
|
|
61
|
-
"ru": "Добавлена обработка перезарядки источника речи, улучшено управление речью по солнечной энергии и времени, добавлены постоянные секунды времени выполнения, улучшена логика восстановления времени выполнения и преобразованы таймеры времени выполнения в таймеры адаптера ioBroker.",
|
|
62
|
-
"pt": "Adicionado tratamento de resfriamento da fonte de fala, controle de fala solar/tempo aprimorado, segundos estados de tempo de execução persistentes adicionados, lógica de restauração de tempo de execução aprimorada e temporizadores de tempo de execução convertidos em temporizadores de adaptador ioBroker.",
|
|
63
|
-
"nl": "Afkoeling van spraakbronnen toegevoegd, verbeterde spraakcontrole op zonne-energie/tijd, persistente tweede runtime-statussen toegevoegd, verbeterde runtime-herstellogica en geconverteerde runtime-timers naar ioBroker-adaptertimers.",
|
|
64
|
-
"fr": "Ajout de la gestion du temps de recharge de la source vocale, du contrôle vocal solaire/temporel amélioré, ajout des seconds états d'exécution persistants, amélioration de la logique de restauration de l'exécution et conversion des minuteries d'exécution en minuteries d'adaptateur ioBroker.",
|
|
65
|
-
"it": "Aggiunta la gestione del raffreddamento della sorgente vocale, controllo vocale solare/ora migliorato, aggiunti secondi stati di runtime persistenti, logica di ripristino del runtime migliorata e timer di runtime convertiti in timer dell'adattatore ioBroker.",
|
|
66
|
-
"es": "Se agregó manejo de enfriamiento de la fuente de voz, control de voz solar/tiempo mejorado, se agregaron segundos estados de tiempo de ejecución persistentes, lógica de restauración de tiempo de ejecución mejorada y temporizadores de tiempo de ejecución convertidos en temporizadores de adaptador ioBroker.",
|
|
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.",
|
|
68
|
-
"uk": "Додано обробку перезарядки джерела мовлення, покращено керування мовленням із сонячними променями/часом, додано постійні другі стани під час виконання, покращено логіку відновлення під час виконання та перетворено таймери часу виконання на таймери адаптера ioBroker.",
|
|
69
|
-
"zh-cn": "添加了语音源冷却处理、改进了太阳/时间语音控制、添加了持久运行时第二状态、改进了运行时恢复逻辑并将运行时计时器转换为 ioBroker 适配器计时器。"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
72
|
"titleLang": {
|
|
@@ -30,10 +30,16 @@ const actuatorsHelper = {
|
|
|
30
30
|
// 🔥 WICHTIG: Actuator-States abonnieren
|
|
31
31
|
this.adapter.subscribeStates('actuators.*');
|
|
32
32
|
|
|
33
|
+
// NEU: Hauptpumpe abonnieren, damit Follow-Pump-Geräte reagieren können
|
|
34
|
+
this.adapter.subscribeStates('pump.pump_switch');
|
|
35
|
+
|
|
33
36
|
// FIX: Config-Werte in interne States übernehmen
|
|
34
37
|
|
|
35
38
|
await this._syncConfigToStates();
|
|
36
39
|
|
|
40
|
+
// NEU: Follow-Pump-Geräte beim Start prüfen und passend setzen
|
|
41
|
+
await this._updateFollowPumpDevices();
|
|
42
|
+
|
|
37
43
|
this.adapter.log.info('[actuatorsHelper] initialized');
|
|
38
44
|
},
|
|
39
45
|
|
|
@@ -41,7 +47,7 @@ const actuatorsHelper = {
|
|
|
41
47
|
// Zentrale StateChange-Anbindung (aus main.js)
|
|
42
48
|
// ======================================================
|
|
43
49
|
async handleStateChange(id, state) {
|
|
44
|
-
if (!state
|
|
50
|
+
if (!state) {
|
|
45
51
|
return;
|
|
46
52
|
}
|
|
47
53
|
|
|
@@ -49,6 +55,26 @@ const actuatorsHelper = {
|
|
|
49
55
|
// FIX: ID normalisieren (poolcontrol.0.x -> x)
|
|
50
56
|
const relId = this._toRelId(id);
|
|
51
57
|
|
|
58
|
+
// NEU: Follow-Pump-Geräte reagieren auch auf ack=true der internen Pumpenlogik
|
|
59
|
+
if (relId === 'pump.pump_switch') {
|
|
60
|
+
await this._updateFollowPumpDevices();
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!relId.startsWith('actuators.')) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (state.ack) {
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// NEU: Änderungen an Follow-Pump-Geräten verarbeiten
|
|
73
|
+
if (relId.startsWith('actuators.follow_pump_devices.')) {
|
|
74
|
+
await this._handleFollowPumpDeviceChange(relId);
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
52
78
|
if (relId.endsWith('.switch')) {
|
|
53
79
|
await this._handleSwitch(relId, !!state.val);
|
|
54
80
|
return;
|
|
@@ -221,6 +247,155 @@ const actuatorsHelper = {
|
|
|
221
247
|
}
|
|
222
248
|
},
|
|
223
249
|
|
|
250
|
+
// ======================================================
|
|
251
|
+
// NEU: Follow-Pump-Geräte
|
|
252
|
+
// ======================================================
|
|
253
|
+
async _handleFollowPumpDeviceChange(relId) {
|
|
254
|
+
const parts = relId.split('.');
|
|
255
|
+
|
|
256
|
+
if (parts.length < 4) {
|
|
257
|
+
return;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
const base = parts.slice(0, 3).join('.');
|
|
261
|
+
|
|
262
|
+
if (relId.endsWith('.enabled') || relId.endsWith('.target_state_id')) {
|
|
263
|
+
await this._validateFollowPumpDevice(base);
|
|
264
|
+
await this._syncFollowPumpDevice(base);
|
|
265
|
+
}
|
|
266
|
+
},
|
|
267
|
+
|
|
268
|
+
async _validateAllFollowPumpDevices() {
|
|
269
|
+
for (let i = 1; i <= 3; i++) {
|
|
270
|
+
await this._validateFollowPumpDevice(`actuators.follow_pump_devices.device${i}`);
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
|
|
274
|
+
async _updateFollowPumpDevices() {
|
|
275
|
+
for (let i = 1; i <= 3; i++) {
|
|
276
|
+
const base = `actuators.follow_pump_devices.device${i}`;
|
|
277
|
+
|
|
278
|
+
await this._validateFollowPumpDevice(base);
|
|
279
|
+
await this._syncFollowPumpDevice(base);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
async _validateFollowPumpDevice(base) {
|
|
284
|
+
const targetId = String((await this._get(`${base}.target_state_id`, '')) || '').trim();
|
|
285
|
+
|
|
286
|
+
if (!targetId) {
|
|
287
|
+
await this._set(`${base}.target_valid`, false);
|
|
288
|
+
await this._set(`${base}.target_writeable`, false);
|
|
289
|
+
await this._set(`${base}.validation_text`, 'target_empty');
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// NEU: Interne PoolControl-Ziele mit Schleifen-/Nebenwirkungsgefahr blockieren
|
|
294
|
+
const namespace = this.adapter?.namespace || '';
|
|
295
|
+
|
|
296
|
+
if (
|
|
297
|
+
namespace &&
|
|
298
|
+
(targetId === `${namespace}.pump.pump_switch` ||
|
|
299
|
+
targetId.startsWith(`${namespace}.actuators.follow_pump_devices.`))
|
|
300
|
+
) {
|
|
301
|
+
await this._set(`${base}.target_valid`, false);
|
|
302
|
+
await this._set(`${base}.target_writeable`, false);
|
|
303
|
+
await this._set(`${base}.validation_text`, 'internal_target_not_allowed');
|
|
304
|
+
return false;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
try {
|
|
308
|
+
const obj = await this.adapter.getForeignObjectAsync(targetId);
|
|
309
|
+
|
|
310
|
+
if (!obj || obj.type !== 'state') {
|
|
311
|
+
await this._set(`${base}.target_valid`, false);
|
|
312
|
+
await this._set(`${base}.target_writeable`, false);
|
|
313
|
+
await this._set(`${base}.validation_text`, 'state_not_found');
|
|
314
|
+
return false;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const isBoolean = obj.common && obj.common.type === 'boolean';
|
|
318
|
+
const isWriteable = obj.common && obj.common.write === true;
|
|
319
|
+
|
|
320
|
+
await this._set(`${base}.target_valid`, isBoolean);
|
|
321
|
+
await this._set(`${base}.target_writeable`, isWriteable);
|
|
322
|
+
|
|
323
|
+
if (!isBoolean) {
|
|
324
|
+
await this._set(`${base}.validation_text`, 'state_is_not_boolean');
|
|
325
|
+
return false;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
if (!isWriteable) {
|
|
329
|
+
await this._set(`${base}.validation_text`, 'state_not_writeable');
|
|
330
|
+
return false;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
await this._set(`${base}.validation_text`, 'OK');
|
|
334
|
+
return true;
|
|
335
|
+
} catch (err) {
|
|
336
|
+
await this._set(`${base}.target_valid`, false);
|
|
337
|
+
await this._set(`${base}.target_writeable`, false);
|
|
338
|
+
await this._set(`${base}.validation_text`, `validation_error: ${err.message}`);
|
|
339
|
+
return false;
|
|
340
|
+
}
|
|
341
|
+
},
|
|
342
|
+
|
|
343
|
+
async _syncFollowPumpDevice(base) {
|
|
344
|
+
const enabled = await this._get(`${base}.enabled`, false);
|
|
345
|
+
const targetValid = await this._get(`${base}.target_valid`, false);
|
|
346
|
+
const targetWriteable = await this._get(`${base}.target_writeable`, false);
|
|
347
|
+
const pumpRunning = await this._get('pump.pump_switch', false);
|
|
348
|
+
|
|
349
|
+
if (!enabled) {
|
|
350
|
+
if (targetValid && targetWriteable) {
|
|
351
|
+
await this._applyFollowPumpTarget(base, false);
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
await this._set(`${base}.active`, false);
|
|
355
|
+
await this._set(`${base}.status`, 'disabled');
|
|
356
|
+
return;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
if (!targetValid || !targetWriteable) {
|
|
360
|
+
await this._set(`${base}.active`, false);
|
|
361
|
+
await this._set(`${base}.status`, 'invalid_target');
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (pumpRunning) {
|
|
366
|
+
await this._applyFollowPumpTarget(base, true);
|
|
367
|
+
await this._set(`${base}.active`, true);
|
|
368
|
+
await this._set(`${base}.status`, 'running_with_pump');
|
|
369
|
+
} else {
|
|
370
|
+
await this._applyFollowPumpTarget(base, false);
|
|
371
|
+
await this._set(`${base}.active`, false);
|
|
372
|
+
await this._set(`${base}.status`, 'waiting_for_pump');
|
|
373
|
+
}
|
|
374
|
+
},
|
|
375
|
+
|
|
376
|
+
async _applyFollowPumpTarget(base, on) {
|
|
377
|
+
const targetId = String((await this._get(`${base}.target_state_id`, '')) || '').trim();
|
|
378
|
+
|
|
379
|
+
if (!targetId) {
|
|
380
|
+
return;
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
try {
|
|
384
|
+
await this.adapter.setForeignStateAsync(targetId, {
|
|
385
|
+
val: !!on,
|
|
386
|
+
ack: false,
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
this.adapter.log.debug(
|
|
390
|
+
`[actuatorsHelper] ${base}: follow-pump target "${targetId}" -> ${on ? 'ON' : 'OFF'}`,
|
|
391
|
+
);
|
|
392
|
+
} catch (err) {
|
|
393
|
+
this.adapter.log.warn(
|
|
394
|
+
`[actuatorsHelper] ${base}: follow-pump target set failed (${targetId}): ${err.message}`,
|
|
395
|
+
);
|
|
396
|
+
}
|
|
397
|
+
},
|
|
398
|
+
|
|
224
399
|
// ======================================================
|
|
225
400
|
// Externes Ziel schalten (aus jsonConfig)
|
|
226
401
|
// ======================================================
|
|
@@ -395,6 +395,198 @@ async function createActuatorsStates(adapter) {
|
|
|
395
395
|
native: {},
|
|
396
396
|
});
|
|
397
397
|
}
|
|
398
|
+
|
|
399
|
+
// ======================================================
|
|
400
|
+
// Follow-Pump Geräte
|
|
401
|
+
// ======================================================
|
|
402
|
+
await adapter.setObjectNotExistsAsync('actuators.follow_pump_devices', {
|
|
403
|
+
type: 'channel',
|
|
404
|
+
common: {
|
|
405
|
+
name: {
|
|
406
|
+
en: 'Follow-pump devices',
|
|
407
|
+
de: 'Geräte mit Pumpenlauf',
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
native: {},
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
for (let i = 1; i <= 3; i++) {
|
|
414
|
+
const base = `actuators.follow_pump_devices.device${i}`;
|
|
415
|
+
|
|
416
|
+
await adapter.setObjectNotExistsAsync(base, {
|
|
417
|
+
type: 'channel',
|
|
418
|
+
common: {
|
|
419
|
+
name: {
|
|
420
|
+
en: `Follow-pump device ${i}`,
|
|
421
|
+
de: `Gerät mit Pumpenlauf ${i}`,
|
|
422
|
+
},
|
|
423
|
+
},
|
|
424
|
+
native: {},
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
await adapter.setObjectNotExistsAsync(`${base}.enabled`, {
|
|
428
|
+
type: 'state',
|
|
429
|
+
common: {
|
|
430
|
+
name: {
|
|
431
|
+
en: 'Enabled',
|
|
432
|
+
de: 'Aktiviert',
|
|
433
|
+
},
|
|
434
|
+
desc: {
|
|
435
|
+
en: 'Enables this follow-pump device. If enabled, the configured target state is switched on when the main pump is running and switched off when the main pump stops.',
|
|
436
|
+
de: 'Aktiviert dieses Gerät mit Pumpenlauf. Wenn aktiviert, wird der eingetragene Ziel-Datenpunkt eingeschaltet, sobald die Hauptpumpe läuft, und ausgeschaltet, sobald die Hauptpumpe stoppt.',
|
|
437
|
+
},
|
|
438
|
+
type: 'boolean',
|
|
439
|
+
role: 'switch',
|
|
440
|
+
read: true,
|
|
441
|
+
write: true,
|
|
442
|
+
def: false,
|
|
443
|
+
persist: true,
|
|
444
|
+
},
|
|
445
|
+
native: {},
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
await adapter.setObjectNotExistsAsync(`${base}.name`, {
|
|
449
|
+
type: 'state',
|
|
450
|
+
common: {
|
|
451
|
+
name: {
|
|
452
|
+
en: 'Name',
|
|
453
|
+
de: 'Name',
|
|
454
|
+
},
|
|
455
|
+
desc: {
|
|
456
|
+
en: 'Custom display name for this follow-pump device, for example UV lamp, water feature or auxiliary filter.',
|
|
457
|
+
de: 'Frei vergebener Anzeigename für dieses Gerät mit Pumpenlauf, zum Beispiel UV-Lampe, Wasserspiel oder Zusatzfilter.',
|
|
458
|
+
},
|
|
459
|
+
type: 'string',
|
|
460
|
+
role: 'text',
|
|
461
|
+
read: true,
|
|
462
|
+
write: true,
|
|
463
|
+
def: '',
|
|
464
|
+
persist: true,
|
|
465
|
+
},
|
|
466
|
+
native: {},
|
|
467
|
+
});
|
|
468
|
+
|
|
469
|
+
await adapter.setObjectNotExistsAsync(`${base}.target_state_id`, {
|
|
470
|
+
type: 'state',
|
|
471
|
+
common: {
|
|
472
|
+
name: {
|
|
473
|
+
en: 'Target state ID',
|
|
474
|
+
de: 'Ziel-Datenpunkt',
|
|
475
|
+
},
|
|
476
|
+
desc: {
|
|
477
|
+
en: 'ioBroker state ID of the device that should follow the main pump. The target state must exist, must be writable and must be of type boolean.',
|
|
478
|
+
de: 'ioBroker-Datenpunkt des Geräts, das dem Lauf der Hauptpumpe folgen soll. Der Ziel-Datenpunkt muss existieren, beschreibbar sein und den Typ boolean haben.',
|
|
479
|
+
},
|
|
480
|
+
type: 'string',
|
|
481
|
+
role: 'text',
|
|
482
|
+
read: true,
|
|
483
|
+
write: true,
|
|
484
|
+
def: '',
|
|
485
|
+
persist: true,
|
|
486
|
+
},
|
|
487
|
+
native: {},
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
await adapter.setObjectNotExistsAsync(`${base}.target_valid`, {
|
|
491
|
+
type: 'state',
|
|
492
|
+
common: {
|
|
493
|
+
name: {
|
|
494
|
+
en: 'Target valid',
|
|
495
|
+
de: 'Ziel gültig',
|
|
496
|
+
},
|
|
497
|
+
desc: {
|
|
498
|
+
en: 'Shows whether the configured target state exists and is of type boolean.',
|
|
499
|
+
de: 'Zeigt an, ob der eingetragene Ziel-Datenpunkt existiert und vom Typ boolean ist.',
|
|
500
|
+
},
|
|
501
|
+
type: 'boolean',
|
|
502
|
+
role: 'indicator',
|
|
503
|
+
read: true,
|
|
504
|
+
write: false,
|
|
505
|
+
def: false,
|
|
506
|
+
},
|
|
507
|
+
native: {},
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
await adapter.setObjectNotExistsAsync(`${base}.target_writeable`, {
|
|
511
|
+
type: 'state',
|
|
512
|
+
common: {
|
|
513
|
+
name: {
|
|
514
|
+
en: 'Target writable',
|
|
515
|
+
de: 'Ziel beschreibbar',
|
|
516
|
+
},
|
|
517
|
+
desc: {
|
|
518
|
+
en: 'Shows whether the configured target state can be written by PoolControl.',
|
|
519
|
+
de: 'Zeigt an, ob der eingetragene Ziel-Datenpunkt von PoolControl beschrieben werden kann.',
|
|
520
|
+
},
|
|
521
|
+
type: 'boolean',
|
|
522
|
+
role: 'indicator',
|
|
523
|
+
read: true,
|
|
524
|
+
write: false,
|
|
525
|
+
def: false,
|
|
526
|
+
},
|
|
527
|
+
native: {},
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
await adapter.setObjectNotExistsAsync(`${base}.validation_text`, {
|
|
531
|
+
type: 'state',
|
|
532
|
+
common: {
|
|
533
|
+
name: {
|
|
534
|
+
en: 'Validation text',
|
|
535
|
+
de: 'Prüfmeldung',
|
|
536
|
+
},
|
|
537
|
+
desc: {
|
|
538
|
+
en: 'Readable validation result for the configured target state, for example OK, target empty, state not found, state is not boolean or state is not writable.',
|
|
539
|
+
de: 'Lesbare Prüfausgabe für den eingetragenen Ziel-Datenpunkt, zum Beispiel OK, Ziel leer, Datenpunkt nicht gefunden, Datenpunkt ist nicht boolean oder Datenpunkt ist nicht beschreibbar.',
|
|
540
|
+
},
|
|
541
|
+
type: 'string',
|
|
542
|
+
role: 'text',
|
|
543
|
+
read: true,
|
|
544
|
+
write: false,
|
|
545
|
+
def: '',
|
|
546
|
+
},
|
|
547
|
+
native: {},
|
|
548
|
+
});
|
|
549
|
+
|
|
550
|
+
await adapter.setObjectNotExistsAsync(`${base}.active`, {
|
|
551
|
+
type: 'state',
|
|
552
|
+
common: {
|
|
553
|
+
name: {
|
|
554
|
+
en: 'Active with pump',
|
|
555
|
+
de: 'Mit Pumpe aktiv',
|
|
556
|
+
},
|
|
557
|
+
desc: {
|
|
558
|
+
en: 'Shows whether this follow-pump device is currently switched on by PoolControl because the main pump is running.',
|
|
559
|
+
de: 'Zeigt an, ob dieses Gerät aktuell von PoolControl eingeschaltet wurde, weil die Hauptpumpe läuft.',
|
|
560
|
+
},
|
|
561
|
+
type: 'boolean',
|
|
562
|
+
role: 'indicator',
|
|
563
|
+
read: true,
|
|
564
|
+
write: false,
|
|
565
|
+
def: false,
|
|
566
|
+
},
|
|
567
|
+
native: {},
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
await adapter.setObjectNotExistsAsync(`${base}.status`, {
|
|
571
|
+
type: 'state',
|
|
572
|
+
common: {
|
|
573
|
+
name: {
|
|
574
|
+
en: 'Status',
|
|
575
|
+
de: 'Status',
|
|
576
|
+
},
|
|
577
|
+
desc: {
|
|
578
|
+
en: 'Current status of this follow-pump device, for example disabled, waiting_for_pump, running_with_pump or invalid_target.',
|
|
579
|
+
de: 'Aktueller Status dieses Geräts mit Pumpenlauf, zum Beispiel deaktiviert, wartet auf Pumpenlauf, läuft mit Pumpe oder ungültiger Ziel-Datenpunkt.',
|
|
580
|
+
},
|
|
581
|
+
type: 'string',
|
|
582
|
+
role: 'text',
|
|
583
|
+
read: true,
|
|
584
|
+
write: false,
|
|
585
|
+
def: '',
|
|
586
|
+
},
|
|
587
|
+
native: {},
|
|
588
|
+
});
|
|
589
|
+
}
|
|
398
590
|
}
|
|
399
591
|
|
|
400
592
|
module.exports = {
|
|
@@ -21,6 +21,16 @@ async function createTimeStates(adapter) {
|
|
|
21
21
|
native: {},
|
|
22
22
|
});
|
|
23
23
|
|
|
24
|
+
async function setInitialStateIfMissing(id, value) {
|
|
25
|
+
const current = await adapter.getStateAsync(id);
|
|
26
|
+
if (!current || current.val === null || current.val === undefined) {
|
|
27
|
+
await adapter.setStateAsync(id, {
|
|
28
|
+
val: value,
|
|
29
|
+
ack: true,
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
24
34
|
async function createTimeWindow(prefix, label) {
|
|
25
35
|
// Aktiv
|
|
26
36
|
await adapter.setObjectNotExistsAsync(`timecontrol.${prefix}_active`, {
|
|
@@ -38,13 +48,13 @@ async function createTimeStates(adapter) {
|
|
|
38
48
|
role: 'switch',
|
|
39
49
|
read: true,
|
|
40
50
|
write: true,
|
|
51
|
+
persist: true,
|
|
41
52
|
},
|
|
42
53
|
native: {},
|
|
43
54
|
});
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
});
|
|
55
|
+
|
|
56
|
+
// FIX: Initialwert nur setzen, wenn noch kein Statewert vorhanden ist
|
|
57
|
+
await setInitialStateIfMissing(`timecontrol.${prefix}_active`, !!adapter.config[`${prefix}_active`]);
|
|
48
58
|
|
|
49
59
|
// Startzeit
|
|
50
60
|
await adapter.setObjectNotExistsAsync(`timecontrol.${prefix}_start`, {
|
|
@@ -62,13 +72,12 @@ async function createTimeStates(adapter) {
|
|
|
62
72
|
role: 'level',
|
|
63
73
|
read: true,
|
|
64
74
|
write: true,
|
|
75
|
+
persist: true,
|
|
65
76
|
},
|
|
66
77
|
native: {},
|
|
67
78
|
});
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
ack: true,
|
|
71
|
-
});
|
|
79
|
+
// FIX: Initialwert nur setzen, wenn noch kein Statewert vorhanden ist
|
|
80
|
+
await setInitialStateIfMissing(`timecontrol.${prefix}_start`, adapter.config[`${prefix}_start`] || '00:00');
|
|
72
81
|
|
|
73
82
|
// Endzeit
|
|
74
83
|
await adapter.setObjectNotExistsAsync(`timecontrol.${prefix}_end`, {
|
|
@@ -86,13 +95,12 @@ async function createTimeStates(adapter) {
|
|
|
86
95
|
role: 'level',
|
|
87
96
|
read: true,
|
|
88
97
|
write: true,
|
|
98
|
+
persist: true,
|
|
89
99
|
},
|
|
90
100
|
native: {},
|
|
91
101
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
ack: true,
|
|
95
|
-
});
|
|
102
|
+
// FIX: Initialwert nur setzen, wenn noch kein Statewert vorhanden ist
|
|
103
|
+
await setInitialStateIfMissing(`timecontrol.${prefix}_end`, adapter.config[`${prefix}_end`] || '00:00');
|
|
96
104
|
|
|
97
105
|
// Wochentage
|
|
98
106
|
const days = [
|
|
@@ -121,13 +129,15 @@ async function createTimeStates(adapter) {
|
|
|
121
129
|
role: 'switch',
|
|
122
130
|
read: true,
|
|
123
131
|
write: true,
|
|
132
|
+
persist: true,
|
|
124
133
|
},
|
|
125
134
|
native: {},
|
|
126
135
|
});
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
136
|
+
// FIX: Initialwert nur setzen, wenn noch kein Statewert vorhanden ist
|
|
137
|
+
await setInitialStateIfMissing(
|
|
138
|
+
`timecontrol.${prefix}_day_${key}`,
|
|
139
|
+
!!adapter.config[`${prefix}_day_${key}`],
|
|
140
|
+
);
|
|
131
141
|
}
|
|
132
142
|
}
|
|
133
143
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.poolcontrol",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.21",
|
|
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",
|