iobroker.poolcontrol 0.6.0 → 0.6.2
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 +7 -25
- package/admin/egon_blaumann.jpg +0 -0
- package/admin/jsonConfig.json +211 -86
- package/io-package.json +25 -26
- package/lib/helpers/migrationHelper.js +44 -34
- package/lib/helpers/photovoltaicHelper.js +40 -8
- package/lib/helpers/speechHelper.js +82 -38
- package/lib/helpers/statisticsHelperMonth.js +20 -0
- package/lib/stateDefinitions/speechStates.js +87 -0
- package/lib/stateDefinitions/statisticsStates.js +10 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -135,6 +135,13 @@ Neue Funktionen werden regelmäßig ergänzt – bitte den Changelog beachten.
|
|
|
135
135
|
## Changelog
|
|
136
136
|
### **WORK IN PROGRESS**
|
|
137
137
|
|
|
138
|
+
## v0.6.2 (2025-11-07)
|
|
139
|
+
- Überarbeitung der Instanzübersicht mit neuen Header-Strukturen für klarere Bedienung
|
|
140
|
+
- Neues Startseitenbild „Egon im Blaumann“ in der Admin-Oberfläche integriert
|
|
141
|
+
- Erweiterung des Sprachsystems um konfigurierbare Alexa-Ausgabezeiten
|
|
142
|
+
- Anpassungen und Aufräumarbeiten in jsonConfig, speechHelper und speechStates
|
|
143
|
+
|
|
144
|
+
|
|
138
145
|
## v0.6.0 (2025-11-03)
|
|
139
146
|
- Einführung der vollständigen Photovoltaik-Steuerung mit automatischer Pumpenlogik
|
|
140
147
|
(neuer Pumpenmodus `Automatik (PV)` unter `pump.mode`)
|
|
@@ -185,31 +192,6 @@ Neue Funktionen werden regelmäßig ergänzt – bitte den Changelog beachten.
|
|
|
185
192
|
Diese Version bildet die stabile Basis für alle folgenden Statistik- und Analysefunktionen
|
|
186
193
|
(z. B. Wochen- und Monatsstatistik, Historien- und Effizienz-Auswertungen).
|
|
187
194
|
|
|
188
|
-
---
|
|
189
|
-
|
|
190
|
-
### 0.3.1 (2025-10-18)
|
|
191
|
-
- FrostHelper stabilisiert:
|
|
192
|
-
- Feste Hysterese von +2 °C (bisher +1 °C)
|
|
193
|
-
- Ganzzahl-Rundung eingeführt zur Vermeidung von Schaltflattern um 3 °C
|
|
194
|
-
- Keine Änderungen an States oder Konfiguration erforderlich
|
|
195
|
-
|
|
196
|
-
---
|
|
197
|
-
|
|
198
|
-
### 0.3.0 (12.10.2025)
|
|
199
|
-
**Neu:** Intelligentes Pumpen-Monitoring & Lernsystem
|
|
200
|
-
|
|
201
|
-
- Hinzugefügt: **Reelle Durchflussberechnung** auf Basis der tatsächlichen Leistungsaufnahme.
|
|
202
|
-
- Neuer Bereich **`pump.live`** zur Live-Überwachung von Leistung, Durchfluss und prozentualer Auslastung.
|
|
203
|
-
- Die **tägliche Umwälzberechnung** verwendet nun den realen Durchflusswert anstelle eines Fixwerts.
|
|
204
|
-
- Neuer Lernbereich **`pump.learning`**:
|
|
205
|
-
- Lernt automatisch die durchschnittlichen Leistungs- und Durchflusswerte.
|
|
206
|
-
- Bestimmt daraus einen dynamischen **Normalbereich (± 15 %)**.
|
|
207
|
-
- Berechnet prozentuale Abweichungen und erstellt **textbasierte Statusmeldungen**.
|
|
208
|
-
- Alle Lernwerte werden **persistent** gespeichert und bleiben auch nach Neustart erhalten.
|
|
209
|
-
- Vollständig **ereignisgesteuerte Logik** ohne zusätzliche Timer oder Polling-Zyklen.
|
|
210
|
-
|
|
211
|
-
> Mit dieser Version beginnt die lernfähige Phase des PoolControl-Adapters:
|
|
212
|
-
> Deine Pumpe weiß jetzt selbst, was für sie „normal“ ist.
|
|
213
195
|
|
|
214
196
|
|
|
215
197
|
*(ältere Versionen siehe [io-package.json](./io-package.json))*
|
|
Binary file
|
package/admin/jsonConfig.json
CHANGED
|
@@ -6,9 +6,16 @@
|
|
|
6
6
|
"type": "panel",
|
|
7
7
|
"label": "Allgemeine Einstellungen",
|
|
8
8
|
"items": {
|
|
9
|
-
"
|
|
10
|
-
"type": "
|
|
11
|
-
"
|
|
9
|
+
"general_header": {
|
|
10
|
+
"type": "header",
|
|
11
|
+
"text": "Allgemeine Einstellungen zum Pool",
|
|
12
|
+
"size": 4,
|
|
13
|
+
"newLine": true,
|
|
14
|
+
"xs": 12,
|
|
15
|
+
"sm": 12,
|
|
16
|
+
"md": 12,
|
|
17
|
+
"lg": 12,
|
|
18
|
+
"xl": 12
|
|
12
19
|
},
|
|
13
20
|
"pool_name": {
|
|
14
21
|
"type": "text",
|
|
@@ -18,7 +25,8 @@
|
|
|
18
25
|
"sm": 3,
|
|
19
26
|
"md": 3,
|
|
20
27
|
"lg": 3,
|
|
21
|
-
"xl": 3
|
|
28
|
+
"xl": 3,
|
|
29
|
+
"newLine": true
|
|
22
30
|
},
|
|
23
31
|
"pool_size": {
|
|
24
32
|
"type": "number",
|
|
@@ -47,12 +55,18 @@
|
|
|
47
55
|
"sm": 3,
|
|
48
56
|
"md": 3,
|
|
49
57
|
"lg": 3,
|
|
50
|
-
"xl": 3
|
|
51
|
-
"newLine": true
|
|
58
|
+
"xl": 3
|
|
52
59
|
},
|
|
53
|
-
|
|
54
|
-
"type": "
|
|
55
|
-
"
|
|
60
|
+
"general_season_header": {
|
|
61
|
+
"type": "header",
|
|
62
|
+
"text": "Saisoneinstellungen",
|
|
63
|
+
"size": 4,
|
|
64
|
+
"newLine": true,
|
|
65
|
+
"xs": 12,
|
|
66
|
+
"sm": 12,
|
|
67
|
+
"md": 12,
|
|
68
|
+
"lg": 12,
|
|
69
|
+
"xl": 12
|
|
56
70
|
},
|
|
57
71
|
"season_active": {
|
|
58
72
|
"type": "checkbox",
|
|
@@ -65,19 +79,32 @@
|
|
|
65
79
|
"xl": 3,
|
|
66
80
|
"newLine": true
|
|
67
81
|
},
|
|
68
|
-
|
|
69
|
-
"type": "
|
|
70
|
-
"
|
|
71
|
-
|
|
82
|
+
"egon_image": {
|
|
83
|
+
"type": "staticImage",
|
|
84
|
+
"src": "adapter/poolcontrol/admin/egon_blaumann.jpg",
|
|
85
|
+
"newLine": true,
|
|
86
|
+
"xs": 3,
|
|
87
|
+
"sm": 3,
|
|
88
|
+
"md": 3,
|
|
89
|
+
"lg": 3,
|
|
90
|
+
"xl": 3
|
|
91
|
+
},
|
|
72
92
|
}
|
|
73
93
|
},
|
|
74
94
|
"pump": {
|
|
75
95
|
"type": "panel",
|
|
76
96
|
"label": "Pumpe",
|
|
77
97
|
"items": {
|
|
78
|
-
"
|
|
79
|
-
"type": "
|
|
80
|
-
"
|
|
98
|
+
"Leistung_header": {
|
|
99
|
+
"type": "header",
|
|
100
|
+
"text": "Pumpeneinstellungen (Nennleistung)",
|
|
101
|
+
"size": 4,
|
|
102
|
+
"newLine": true,
|
|
103
|
+
"xs": 12,
|
|
104
|
+
"sm": 12,
|
|
105
|
+
"md": 12,
|
|
106
|
+
"lg": 12,
|
|
107
|
+
"xl": 12
|
|
81
108
|
},
|
|
82
109
|
"pump_max_watt": {
|
|
83
110
|
"type": "number",
|
|
@@ -87,7 +114,8 @@
|
|
|
87
114
|
"sm": 3,
|
|
88
115
|
"md": 3,
|
|
89
116
|
"lg": 3,
|
|
90
|
-
"xl": 3
|
|
117
|
+
"xl": 3,
|
|
118
|
+
"newLine": true
|
|
91
119
|
},
|
|
92
120
|
"pump_power_lph": {
|
|
93
121
|
"type": "number",
|
|
@@ -97,12 +125,18 @@
|
|
|
97
125
|
"sm": 3,
|
|
98
126
|
"md": 3,
|
|
99
127
|
"lg": 3,
|
|
100
|
-
"xl": 3
|
|
101
|
-
"newLine": true
|
|
128
|
+
"xl": 3
|
|
102
129
|
},
|
|
103
|
-
"
|
|
104
|
-
"type": "
|
|
105
|
-
"
|
|
130
|
+
"Frostschutz_header": {
|
|
131
|
+
"type": "header",
|
|
132
|
+
"text": "Frostschutz",
|
|
133
|
+
"size": 4,
|
|
134
|
+
"newLine": true,
|
|
135
|
+
"xs": 12,
|
|
136
|
+
"sm": 12,
|
|
137
|
+
"md": 12,
|
|
138
|
+
"lg": 12,
|
|
139
|
+
"xl": 12
|
|
106
140
|
},
|
|
107
141
|
"frost_protection_active": {
|
|
108
142
|
"type": "checkbox",
|
|
@@ -125,9 +159,16 @@
|
|
|
125
159
|
"lg": 3,
|
|
126
160
|
"xl": 3
|
|
127
161
|
},
|
|
128
|
-
"
|
|
129
|
-
"type": "
|
|
130
|
-
"
|
|
162
|
+
"pumpswitch_header": {
|
|
163
|
+
"type": "header",
|
|
164
|
+
"text": "Pumpendatenpunkte (Messsteckdose & Aktueller Verbrauch Watt)",
|
|
165
|
+
"size": 4,
|
|
166
|
+
"newLine": true,
|
|
167
|
+
"xs": 12,
|
|
168
|
+
"sm": 12,
|
|
169
|
+
"md": 12,
|
|
170
|
+
"lg": 12,
|
|
171
|
+
"xl": 12
|
|
131
172
|
},
|
|
132
173
|
"pump_switch": {
|
|
133
174
|
"type": "objectId",
|
|
@@ -149,10 +190,6 @@
|
|
|
149
190
|
"lg": 3,
|
|
150
191
|
"xl": 3
|
|
151
192
|
},
|
|
152
|
-
"divider9": {
|
|
153
|
-
"type": "divider",
|
|
154
|
-
"newLine": true
|
|
155
|
-
},
|
|
156
193
|
"pump_mode_hint": {
|
|
157
194
|
"type": "staticText",
|
|
158
195
|
"text": "Pumpenmodus (Automatik/Manuell/Aus/Zeit) kann über den Datenpunkt 'pump.mode' gesteuert werden.",
|
|
@@ -163,9 +200,16 @@
|
|
|
163
200
|
"lg": 12,
|
|
164
201
|
"xl": 12
|
|
165
202
|
},
|
|
166
|
-
"
|
|
167
|
-
"type": "
|
|
168
|
-
"
|
|
203
|
+
"pump_safety_header": {
|
|
204
|
+
"type": "header",
|
|
205
|
+
"text": "Sicherheitsfunktionen",
|
|
206
|
+
"size": 4,
|
|
207
|
+
"newLine": true,
|
|
208
|
+
"xs": 12,
|
|
209
|
+
"sm": 12,
|
|
210
|
+
"md": 12,
|
|
211
|
+
"lg": 12,
|
|
212
|
+
"xl": 12
|
|
169
213
|
},
|
|
170
214
|
"manual_safety_enabled": {
|
|
171
215
|
"type": "checkbox",
|
|
@@ -194,9 +238,16 @@
|
|
|
194
238
|
"type": "panel",
|
|
195
239
|
"label": "Temperaturverwaltung",
|
|
196
240
|
"items": {
|
|
197
|
-
"
|
|
198
|
-
"type": "
|
|
199
|
-
"
|
|
241
|
+
"tempsensor_header": {
|
|
242
|
+
"type": "header",
|
|
243
|
+
"text": "Temperatursensoren",
|
|
244
|
+
"size": 4,
|
|
245
|
+
"newLine": true,
|
|
246
|
+
"xs": 12,
|
|
247
|
+
"sm": 12,
|
|
248
|
+
"md": 12,
|
|
249
|
+
"lg": 12,
|
|
250
|
+
"xl": 12
|
|
200
251
|
},
|
|
201
252
|
"surface_temp_active": {
|
|
202
253
|
"type": "checkbox",
|
|
@@ -330,9 +381,16 @@
|
|
|
330
381
|
"type": "panel",
|
|
331
382
|
"label": "Solarverwaltung",
|
|
332
383
|
"items": {
|
|
333
|
-
"
|
|
334
|
-
"type": "
|
|
335
|
-
"
|
|
384
|
+
"solar_control_active_header": {
|
|
385
|
+
"type": "header",
|
|
386
|
+
"text": "Solarsteuerung aktivieren",
|
|
387
|
+
"size": 4,
|
|
388
|
+
"newLine": true,
|
|
389
|
+
"xs": 12,
|
|
390
|
+
"sm": 12,
|
|
391
|
+
"md": 12,
|
|
392
|
+
"lg": 12,
|
|
393
|
+
"xl": 12
|
|
336
394
|
},
|
|
337
395
|
"solar_control_active": {
|
|
338
396
|
"type": "checkbox",
|
|
@@ -345,9 +403,16 @@
|
|
|
345
403
|
"xl": 3,
|
|
346
404
|
"newLine": true
|
|
347
405
|
},
|
|
348
|
-
"
|
|
349
|
-
"type": "
|
|
350
|
-
"
|
|
406
|
+
"solar_hysteresis_active_header": {
|
|
407
|
+
"type": "header",
|
|
408
|
+
"text": "Hysterese",
|
|
409
|
+
"size": 4,
|
|
410
|
+
"newLine": true,
|
|
411
|
+
"xs": 12,
|
|
412
|
+
"sm": 12,
|
|
413
|
+
"md": 12,
|
|
414
|
+
"lg": 12,
|
|
415
|
+
"xl": 12
|
|
351
416
|
},
|
|
352
417
|
"solar_hysteresis_active": {
|
|
353
418
|
"type": "checkbox",
|
|
@@ -380,10 +445,17 @@
|
|
|
380
445
|
"lg": 3,
|
|
381
446
|
"xl": 3
|
|
382
447
|
},
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
448
|
+
"solar_collector_warn_active_header": {
|
|
449
|
+
"type": "header",
|
|
450
|
+
"text": "Kollektortemperatur-Warnung",
|
|
451
|
+
"size": 4,
|
|
452
|
+
"newLine": true,
|
|
453
|
+
"xs": 12,
|
|
454
|
+
"sm": 12,
|
|
455
|
+
"md": 12,
|
|
456
|
+
"lg": 12,
|
|
457
|
+
"xl": 12
|
|
458
|
+
},
|
|
387
459
|
"solar_collector_warn_active": {
|
|
388
460
|
"type": "checkbox",
|
|
389
461
|
"label": "Kollektortemperatur-Warnung aktivieren",
|
|
@@ -432,9 +504,16 @@
|
|
|
432
504
|
"type": "panel",
|
|
433
505
|
"label": "Zeitsteuerung",
|
|
434
506
|
"items": {
|
|
435
|
-
"
|
|
436
|
-
"type": "
|
|
437
|
-
"
|
|
507
|
+
"time1_active_header": {
|
|
508
|
+
"type": "header",
|
|
509
|
+
"text": "Zeitfenster 1",
|
|
510
|
+
"size": 4,
|
|
511
|
+
"newLine": true,
|
|
512
|
+
"xs": 12,
|
|
513
|
+
"sm": 12,
|
|
514
|
+
"md": 12,
|
|
515
|
+
"lg": 12,
|
|
516
|
+
"xl": 12
|
|
438
517
|
},
|
|
439
518
|
"time1_active": {
|
|
440
519
|
"type": "checkbox",
|
|
@@ -538,9 +617,16 @@
|
|
|
538
617
|
"lg": 1,
|
|
539
618
|
"xl": 1
|
|
540
619
|
},
|
|
541
|
-
"
|
|
542
|
-
"type": "
|
|
543
|
-
"
|
|
620
|
+
"time2_active_header": {
|
|
621
|
+
"type": "header",
|
|
622
|
+
"text": "Zeitfenster 2",
|
|
623
|
+
"size": 4,
|
|
624
|
+
"newLine": true,
|
|
625
|
+
"xs": 12,
|
|
626
|
+
"sm": 12,
|
|
627
|
+
"md": 12,
|
|
628
|
+
"lg": 12,
|
|
629
|
+
"xl": 12
|
|
544
630
|
},
|
|
545
631
|
"time2_active": {
|
|
546
632
|
"type": "checkbox",
|
|
@@ -644,9 +730,16 @@
|
|
|
644
730
|
"lg": 1,
|
|
645
731
|
"xl": 1
|
|
646
732
|
},
|
|
647
|
-
"
|
|
648
|
-
"type": "
|
|
649
|
-
"
|
|
733
|
+
"time3_active_header": {
|
|
734
|
+
"type": "header",
|
|
735
|
+
"text": "Zeitfenster 3",
|
|
736
|
+
"size": 4,
|
|
737
|
+
"newLine": true,
|
|
738
|
+
"xs": 12,
|
|
739
|
+
"sm": 12,
|
|
740
|
+
"md": 12,
|
|
741
|
+
"lg": 12,
|
|
742
|
+
"xl": 12
|
|
650
743
|
},
|
|
651
744
|
"time3_active": {
|
|
652
745
|
"type": "checkbox",
|
|
@@ -756,10 +849,17 @@
|
|
|
756
849
|
"type": "panel",
|
|
757
850
|
"label": "Sprachausgaben",
|
|
758
851
|
"items": {
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
852
|
+
"speech_general_header": {
|
|
853
|
+
"type": "header",
|
|
854
|
+
"text": "Generell",
|
|
855
|
+
"size": 4,
|
|
856
|
+
"newLine": true,
|
|
857
|
+
"xs": 12,
|
|
858
|
+
"sm": 12,
|
|
859
|
+
"md": 12,
|
|
860
|
+
"lg": 12,
|
|
861
|
+
"xl": 12
|
|
862
|
+
},
|
|
763
863
|
"speech_active": {
|
|
764
864
|
"type": "checkbox",
|
|
765
865
|
"label": "Sprachausgaben aktivieren",
|
|
@@ -772,10 +872,17 @@
|
|
|
772
872
|
"newLine": true
|
|
773
873
|
},
|
|
774
874
|
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
875
|
+
"speech_alexa_header": {
|
|
876
|
+
"type": "header",
|
|
877
|
+
"text": "Amazon Alexa",
|
|
878
|
+
"size": 4,
|
|
879
|
+
"newLine": true,
|
|
880
|
+
"xs": 12,
|
|
881
|
+
"sm": 12,
|
|
882
|
+
"md": 12,
|
|
883
|
+
"lg": 12,
|
|
884
|
+
"xl": 12
|
|
885
|
+
},
|
|
779
886
|
"speech_alexa_enabled": {
|
|
780
887
|
"type": "checkbox",
|
|
781
888
|
"label": "Ausgabe über Alexa aktivieren",
|
|
@@ -823,10 +930,17 @@
|
|
|
823
930
|
"xl": 3
|
|
824
931
|
},
|
|
825
932
|
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
933
|
+
"speech_telegram_header": {
|
|
934
|
+
"type": "header",
|
|
935
|
+
"text": "Telegram",
|
|
936
|
+
"size": 4,
|
|
937
|
+
"newLine": true,
|
|
938
|
+
"xs": 12,
|
|
939
|
+
"sm": 12,
|
|
940
|
+
"md": 12,
|
|
941
|
+
"lg": 12,
|
|
942
|
+
"xl": 12
|
|
943
|
+
},
|
|
830
944
|
"speech_telegram_enabled": {
|
|
831
945
|
"type": "checkbox",
|
|
832
946
|
"label": "Ausgabe über Telegram aktivieren",
|
|
@@ -870,9 +984,16 @@
|
|
|
870
984
|
"lg": 6,
|
|
871
985
|
"xl": 6
|
|
872
986
|
},
|
|
873
|
-
|
|
874
|
-
"type": "
|
|
875
|
-
"
|
|
987
|
+
"speech_mail_header": {
|
|
988
|
+
"type": "header",
|
|
989
|
+
"text": "Mail",
|
|
990
|
+
"size": 4,
|
|
991
|
+
"newLine": true,
|
|
992
|
+
"xs": 12,
|
|
993
|
+
"sm": 12,
|
|
994
|
+
"md": 12,
|
|
995
|
+
"lg": 12,
|
|
996
|
+
"xl": 12
|
|
876
997
|
},
|
|
877
998
|
"speech_email_enabled": {
|
|
878
999
|
"type": "checkbox",
|
|
@@ -948,10 +1069,17 @@
|
|
|
948
1069
|
"type": "panel",
|
|
949
1070
|
"label": "PV, Verbrauch & Kosten",
|
|
950
1071
|
"items": {
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
1072
|
+
"consumption_header": {
|
|
1073
|
+
"type": "header",
|
|
1074
|
+
"text": "Verbrauch- und Kostenberechnung",
|
|
1075
|
+
"size": 4,
|
|
1076
|
+
"newLine": true,
|
|
1077
|
+
"xs": 12,
|
|
1078
|
+
"sm": 12,
|
|
1079
|
+
"md": 12,
|
|
1080
|
+
"lg": 12,
|
|
1081
|
+
"xl": 12
|
|
1082
|
+
},
|
|
955
1083
|
"consumption_enabled": {
|
|
956
1084
|
"type": "checkbox",
|
|
957
1085
|
"label": "Verbrauchs- und Kostenberechnung aktivieren",
|
|
@@ -974,11 +1102,6 @@
|
|
|
974
1102
|
"lg": 6,
|
|
975
1103
|
"xl": 6
|
|
976
1104
|
},
|
|
977
|
-
|
|
978
|
-
"divider_cons2": {
|
|
979
|
-
"type": "divider",
|
|
980
|
-
"newLine": true
|
|
981
|
-
},
|
|
982
1105
|
"energy_price_eur_kwh": {
|
|
983
1106
|
"type": "number",
|
|
984
1107
|
"label": "Strompreis (€ / kWh)",
|
|
@@ -1003,11 +1126,6 @@
|
|
|
1003
1126
|
"lg": 12,
|
|
1004
1127
|
"xl": 12
|
|
1005
1128
|
},
|
|
1006
|
-
|
|
1007
|
-
"divider_pv1": {
|
|
1008
|
-
"type": "divider",
|
|
1009
|
-
"newLine": true
|
|
1010
|
-
},
|
|
1011
1129
|
"pv_header": {
|
|
1012
1130
|
"type": "header",
|
|
1013
1131
|
"text": "Photovoltaik (Überschusserkennung)",
|
|
@@ -1061,9 +1179,16 @@
|
|
|
1061
1179
|
"type": "panel",
|
|
1062
1180
|
"label": "Hilfe",
|
|
1063
1181
|
"items": {
|
|
1064
|
-
"
|
|
1065
|
-
"type": "
|
|
1066
|
-
"
|
|
1182
|
+
"help_header": {
|
|
1183
|
+
"type": "header",
|
|
1184
|
+
"text": "Hilfe und Hinweise",
|
|
1185
|
+
"size": 4,
|
|
1186
|
+
"newLine": true,
|
|
1187
|
+
"xs": 12,
|
|
1188
|
+
"sm": 12,
|
|
1189
|
+
"md": 12,
|
|
1190
|
+
"lg": 12,
|
|
1191
|
+
"xl": 12
|
|
1067
1192
|
},
|
|
1068
1193
|
"helpLink": {
|
|
1069
1194
|
"type": "staticLink",
|
package/io-package.json
CHANGED
|
@@ -1,8 +1,32 @@
|
|
|
1
1
|
{
|
|
2
2
|
"common": {
|
|
3
3
|
"name": "poolcontrol",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.2",
|
|
5
5
|
"news": {
|
|
6
|
+
"0.6.2": {
|
|
7
|
+
"en": "UI refinement and admin interface improvements. Added image integration ('Egon in blue overalls') for visual identification. Speech system extended with Alexa output time configuration. Cleaned and optimized jsonConfig with section headers for improved clarity.",
|
|
8
|
+
"de": "Oberflächenüberarbeitung und Verbesserungen der Admin-Ansicht. Bildintegration ('Egon im Blaumann') zur visuellen Wiedererkennung hinzugefügt. Sprachsystem um konfigurierbare Alexa-Ausgabezeiten erweitert. jsonConfig mit Abschnittsüberschriften bereinigt und übersichtlicher gestaltet.",
|
|
9
|
+
"ru": "Улучшение интерфейса и доработка административной панели. Добавлено изображение ('Эгон в синем комбинезоне') для визуальной идентификации. Расширена система речи с настройкой времени вывода Alexa. Очищена и оптимизирована jsonConfig с заголовками разделов для лучшей читаемости.",
|
|
10
|
+
"pt": "Aprimoramento da interface e melhorias na administração. Adicionada imagem ('Egon de macacão azul') para identificação visual. Sistema de fala ampliado com configuração de horários de saída Alexa. jsonConfig limpa e otimizada com cabeçalhos de seção para melhor clareza.",
|
|
11
|
+
"nl": "UI verfijnd en verbeteringen in het beheerpaneel. Afbeelding ('Egon in blauwe overall') toegevoegd voor visuele herkenning. Spraaksysteem uitgebreid met Alexa-uitvoertijdaanpassing. jsonConfig opgeschoond en overzichtelijker gemaakt met sectiekoppen.",
|
|
12
|
+
"fr": "Amélioration de l'interface utilisateur et du panneau d'administration. Image ajoutée ('Egon en bleu de travail') pour identification visuelle. Système vocal étendu avec configuration des heures de sortie Alexa. jsonConfig nettoyé et optimisé avec en-têtes de section pour plus de clarté.",
|
|
13
|
+
"it": "Raffinamento dell'interfaccia e miglioramenti al pannello di amministrazione. Aggiunta l'immagine ('Egon in tuta blu') per l'identificazione visiva. Sistema vocale ampliato con configurazione degli orari di uscita Alexa. jsonConfig ripulito e ottimizzato con intestazioni di sezione per maggiore chiarezza.",
|
|
14
|
+
"es": "Refinamiento de la interfaz y mejoras en el panel de administración. Se agregó imagen ('Egon con mono azul') para identificación visual. Sistema de voz ampliado con configuración de horarios de salida de Alexa. jsonConfig limpiado y optimizado con encabezados de sección para mayor claridad.",
|
|
15
|
+
"pl": "Udoskonalenie interfejsu i panelu administracyjnego. Dodano obraz ('Egon w niebieskim kombinezonie') dla wizualnej identyfikacji. Rozszerzono system mowy o konfigurację czasu wyjścia Alexa. Wyczyczono i zoptymalizowano jsonConfig z nagłówkami sekcji dla lepszej przejrzystości.",
|
|
16
|
+
"zh-cn": "改进了界面和管理界面。添加了图像('蓝色工作服的Egon')以实现视觉识别。语音系统扩展了Alexa输出时间配置。清理并优化了带有章节标题的jsonConfig,使其更清晰。"
|
|
17
|
+
},
|
|
18
|
+
"0.6.1": {
|
|
19
|
+
"en": "Fixed false monthly reset when no last_reset date existed. Corrected real last_update timestamp in monthly statistics.",
|
|
20
|
+
"de": "Fehlerhaften Monatsreset ohne gültiges last_reset-Datum behoben. Korrigierter echter Zeitstempel last_update in der Monatsstatistik.",
|
|
21
|
+
"ru": "Исправлен неверный ежемесячный сброс без действительной даты last_reset. Исправлено обновление времени last_update в ежемесячной статистике.",
|
|
22
|
+
"pt": "Corrigido o reset mensal incorreto quando não havia data last_reset. Corrigido o timestamp real de last_update na estatística mensal.",
|
|
23
|
+
"nl": "Onjuiste maandelijkse reset opgelost wanneer er geen last_reset-datum was. Gecorrigeerde echte last_update-tijd in maandelijkse statistieken.",
|
|
24
|
+
"fr": "Correction du réinitialisation mensuelle erronée lorsqu'aucune date last_reset n'existait. Horodatage last_update réel corrigé dans les statistiques mensuelles.",
|
|
25
|
+
"it": "Corretto il reset mensile errato quando non esisteva una data last_reset. Corretto il timestamp reale last_update nelle statistiche mensili.",
|
|
26
|
+
"es": "Corregido el restablecimiento mensual erróneo cuando no existía una fecha last_reset. Corregida la marca de tiempo real last_update en las estadísticas mensuales.",
|
|
27
|
+
"pl": "Naprawiono błędny miesięczny reset, gdy nie istniała data last_reset. Poprawiono rzeczywisty znacznik czasu last_update w miesięcznych statystykach.",
|
|
28
|
+
"zh-cn": "修复了在不存在 last_reset 日期时错误的月度重置。修正了月度统计中的真实 last_update 时间戳。"
|
|
29
|
+
},
|
|
6
30
|
"0.6.0": {
|
|
7
31
|
"en": "Added complete photovoltaic control with automatic pump management (mode 'Automatik (PV)'). The adapter can now react to PV surplus based on configurable house consumption and generation values. Includes migration for existing installations. Improved system consistency and internal logging.",
|
|
8
32
|
"de": "Vollständige Photovoltaik-Steuerung mit automatischer Pumpenlogik (Modus 'Automatik (PV)') hinzugefügt. Der Adapter reagiert nun auf PV-Überschuss anhand konfigurierbarer Hausverbrauchs- und Erzeugungswerte. Migration für bestehende Installationen integriert. Systemkonsistenz und interne Protokollierung verbessert.",
|
|
@@ -66,31 +90,6 @@
|
|
|
66
90
|
"es": "Sistema de prioridad de helpers ampliado: solucionados los conflictos de tiempo/solar, la protección contra heladas se pausa durante las ventanas de tiempo. Comportamiento estable de la bomba y mejor coordinación entre helpers.",
|
|
67
91
|
"pl": "Rozszerzony system priorytetów helperów: naprawiono konflikty między czasem a sterowaniem solarnym, ochrona przed mrozem wstrzymuje się podczas okien czasowych. Stabilne działanie pompy i lepsza koordynacja między helperami.",
|
|
68
92
|
"zh-cn": "扩展的助手优先级系统:修复时间/太阳能冲突,防冻在时间窗口内暂停。泵运行稳定,助手之间协调更好。"
|
|
69
|
-
},
|
|
70
|
-
"0.5.1": {
|
|
71
|
-
"en": "Extended week and month statistics with persistent data, unified JSON format, and installation protection.",
|
|
72
|
-
"de": "Erweiterte Wochen- und Monatsstatistik mit persistenten Daten, einheitlichem JSON-Format und Überinstallationsschutz.",
|
|
73
|
-
"ru": "Расширенная недельная и месячная статистика с постоянными данными, унифицированным форматом JSON и защитой при переустановке.",
|
|
74
|
-
"pt": "Estatísticas semanais e mensais expandidas com dados persistentes, formato JSON unificado e proteção de reinstalação.",
|
|
75
|
-
"nl": "Uitgebreide week- en maandstatistieken met persistente gegevens, uniforme JSON-indeling en installatiebescherming.",
|
|
76
|
-
"fr": "Statistiques hebdomadaires et mensuelles étendues avec données persistantes, format JSON unifié et protection à la réinstallation.",
|
|
77
|
-
"it": "Statistiche settimanali e mensili estese con dati persistenti, formato JSON unificato e protezione dall'installazione.",
|
|
78
|
-
"es": "Estadísticas semanales y mensuales ampliadas con datos persistentes, formato JSON unificado y protección de reinstalación.",
|
|
79
|
-
"pl": "Rozszerzone statystyki tygodniowe i miesięczne z trwałymi danymi, ujednoliconym formatem JSON i ochroną przed ponowną instalacją.",
|
|
80
|
-
"uk": "Розширена тижнева та місячна статистика з постійними даними, уніфікованим форматом JSON і захистом під час перевстановлення.",
|
|
81
|
-
"zh-cn": "扩展的周和月统计,具有持久数据、统一的 JSON 格式和重新安装保护。"
|
|
82
|
-
},
|
|
83
|
-
"0.5.0": {
|
|
84
|
-
"en": "Added weekly and monthly temperature statistics under analytics.statistics.temperature.week and analytics.statistics.temperature.month with automatic summaries, independent helpers and persistent data points.",
|
|
85
|
-
"de": "Wöchentliche und monatliche Temperaturstatistiken unter analytics.statistics.temperature.week und analytics.statistics.temperature.month hinzugefügt – mit automatischen Zusammenfassungen, unabhängigen Helpern und persistenten Datenpunkten.",
|
|
86
|
-
"ru": "Добавлена еженедельная и ежемесячная статистика температуры в analytics.statistics.temperature.week и analytics.statistics.temperature.month с автоматическими сводками, независимыми помощниками и постоянными точками данных.",
|
|
87
|
-
"fr": "Ajout des statistiques de température hebdomadaires et mensuelles sous analytics.statistics.temperature.week et analytics.statistics.temperature.month avec résumés automatiques, helpers indépendants et points de données persistants.",
|
|
88
|
-
"it": "Aggiunte statistiche settimanali e mensili della temperatura in analytics.statistics.temperature.week e analytics.statistics.temperature.month con riepiloghi automatici, helper indipendenti e punti dati persistenti.",
|
|
89
|
-
"es": "Se añadieron estadísticas semanales y mensuales de temperatura en analytics.statistics.temperature.week y analytics.statistics.temperature.month con resúmenes automáticos, ayudantes independientes y puntos de datos persistentes.",
|
|
90
|
-
"nl": "Wekelijkse en maandelijkse temperatuurstatistieken toegevoegd onder analytics.statistics.temperature.week en analytics.statistics.temperature.month met automatische samenvattingen, onafhankelijke helpers en persistente gegevenspunten.",
|
|
91
|
-
"pl": "Dodano tygodniowe i miesięczne statystyki temperatury w analytics.statistics.temperature.week i analytics.statistics.temperature.month z automatycznymi podsumowaniami, niezależnymi pomocnikami i trwałymi punktami danych.",
|
|
92
|
-
"uk": "Додано щотижневу та щомісячну статистику температури в analytics.statistics.temperature.week та analytics.statistics.temperature.month з автоматичними зведеннями, незалежними помічниками та постійними точками даних.",
|
|
93
|
-
"zh-cn": "在 analytics.statistics.temperature.week 和 analytics.statistics.temperature.month 中添加了每周和每月温度统计,具有自动摘要、独立助手和持久数据点。"
|
|
94
93
|
}
|
|
95
94
|
},
|
|
96
95
|
"titleLang": {
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* - Wird beim Adapterstart einmalig ausgeführt.
|
|
10
10
|
* - Korrigiert veraltete Definitionen (z. B. Schreibrechte, persist-Flags, etc.)
|
|
11
11
|
*
|
|
12
|
-
* Version: 1.0.
|
|
12
|
+
* Version: 1.0.3
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const migrationHelper = {
|
|
@@ -32,10 +32,7 @@ const migrationHelper = {
|
|
|
32
32
|
await this._fixSpeechQueue();
|
|
33
33
|
await this._fixSolarWarnActivePersist();
|
|
34
34
|
await this._fixPumpModeStates(); // NEU: PV-Automatik hinzufügen
|
|
35
|
-
|
|
36
|
-
// Weitere Routinen folgen hier später:
|
|
37
|
-
// await this._ensurePumpReason();
|
|
38
|
-
// await this._cleanupOldStates();
|
|
35
|
+
await this._removeInvalidResetButtons(); // NEU: Entfernt Week/Month-Reset-Buttons
|
|
39
36
|
|
|
40
37
|
this.adapter.log.debug('[migrationHelper] Migration-Checks abgeschlossen.');
|
|
41
38
|
} catch (err) {
|
|
@@ -48,17 +45,11 @@ const migrationHelper = {
|
|
|
48
45
|
// ------------------------------------------------------
|
|
49
46
|
// Migration: Schreibrecht für speech.queue korrigieren
|
|
50
47
|
// ------------------------------------------------------
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Prüft und korrigiert den State "speech.queue", falls er noch write:false gesetzt hat.
|
|
54
|
-
* Dadurch verschwinden Warnungen beim Schreiben (Read-only state ... written without ack).
|
|
55
|
-
*/
|
|
56
48
|
async _fixSpeechQueue() {
|
|
57
49
|
const id = 'speech.queue';
|
|
58
50
|
try {
|
|
59
51
|
const obj = await this.adapter.getObjectAsync(id);
|
|
60
52
|
if (!obj) {
|
|
61
|
-
this.adapter.log.debug(`[migrationHelper] ${id} existiert nicht – keine Anpassung nötig.`);
|
|
62
53
|
return;
|
|
63
54
|
}
|
|
64
55
|
|
|
@@ -71,8 +62,6 @@ const migrationHelper = {
|
|
|
71
62
|
desc: 'Nur intern durch den Adapter beschreibbar (nicht manuell ändern!)',
|
|
72
63
|
},
|
|
73
64
|
});
|
|
74
|
-
} else {
|
|
75
|
-
this.adapter.log.debug(`[migrationHelper] ${id} ist bereits korrekt konfiguriert.`);
|
|
76
65
|
}
|
|
77
66
|
} catch (err) {
|
|
78
67
|
this.adapter.log.warn(`[migrationHelper] Fehler bei Prüfung von ${id}: ${err.message}`);
|
|
@@ -82,18 +71,11 @@ const migrationHelper = {
|
|
|
82
71
|
// ------------------------------------------------------
|
|
83
72
|
// Migration: persist-Flag für solar.warn_active ergänzen
|
|
84
73
|
// ------------------------------------------------------
|
|
85
|
-
|
|
86
|
-
/**
|
|
87
|
-
* Ergänzt persist:true bei solar.warn_active,
|
|
88
|
-
* damit die Einstellung (Warnfunktion aktivieren/deaktivieren)
|
|
89
|
-
* nach einem Neustart erhalten bleibt.
|
|
90
|
-
*/
|
|
91
74
|
async _fixSolarWarnActivePersist() {
|
|
92
75
|
const id = 'solar.warn_active';
|
|
93
76
|
try {
|
|
94
77
|
const obj = await this.adapter.getObjectAsync(id);
|
|
95
78
|
if (!obj) {
|
|
96
|
-
this.adapter.log.debug(`[migrationHelper] ${id} existiert nicht – keine Anpassung nötig.`);
|
|
97
79
|
return;
|
|
98
80
|
}
|
|
99
81
|
|
|
@@ -106,8 +88,6 @@ const migrationHelper = {
|
|
|
106
88
|
desc: `${obj.common?.desc || ''} (automatisch per Migration persistiert)`,
|
|
107
89
|
},
|
|
108
90
|
});
|
|
109
|
-
} else {
|
|
110
|
-
this.adapter.log.debug(`[migrationHelper] ${id} ist bereits mit persist:true versehen.`);
|
|
111
91
|
}
|
|
112
92
|
} catch (err) {
|
|
113
93
|
this.adapter.log.warn(`[migrationHelper] Fehler bei Prüfung von ${id}: ${err.message}`);
|
|
@@ -117,18 +97,11 @@ const migrationHelper = {
|
|
|
117
97
|
// ------------------------------------------------------
|
|
118
98
|
// Migration: Ergänze neuen Pumpenmodus "Automatik (PV)"
|
|
119
99
|
// ------------------------------------------------------
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Prüft den State pump.mode und ergänzt, falls nötig, den neuen
|
|
123
|
-
* Eintrag "auto_pv" → "Automatik (PV)".
|
|
124
|
-
* Dadurch wird der PV-Modus automatisch in bestehenden Installationen sichtbar.
|
|
125
|
-
*/
|
|
126
100
|
async _fixPumpModeStates() {
|
|
127
101
|
const id = 'pump.mode';
|
|
128
102
|
try {
|
|
129
103
|
const obj = await this.adapter.getObjectAsync(id);
|
|
130
104
|
if (!obj) {
|
|
131
|
-
this.adapter.log.debug(`[migrationHelper] ${id} existiert nicht – keine Anpassung nötig.`);
|
|
132
105
|
return;
|
|
133
106
|
}
|
|
134
107
|
|
|
@@ -136,16 +109,53 @@ const migrationHelper = {
|
|
|
136
109
|
if (!states.auto_pv) {
|
|
137
110
|
states.auto_pv = 'Automatik (PV)';
|
|
138
111
|
this.adapter.log.info(`[migrationHelper] Ergänze neuen Modus "Automatik (PV)" in pump.mode`);
|
|
139
|
-
await this.adapter.extendObjectAsync(id, {
|
|
140
|
-
common: { states },
|
|
141
|
-
});
|
|
142
|
-
} else {
|
|
143
|
-
this.adapter.log.debug(`[migrationHelper] pump.mode enthält bereits "Automatik (PV)".`);
|
|
112
|
+
await this.adapter.extendObjectAsync(id, { common: { states } });
|
|
144
113
|
}
|
|
145
114
|
} catch (err) {
|
|
146
115
|
this.adapter.log.warn(`[migrationHelper] Fehler bei Prüfung von ${id}: ${err.message}`);
|
|
147
116
|
}
|
|
148
117
|
},
|
|
118
|
+
|
|
119
|
+
// FIX: Entferne versehentlich angelegte Reset-Buttons aus Wochen- und Monatsstatistik
|
|
120
|
+
async _removeInvalidResetButtons() {
|
|
121
|
+
try {
|
|
122
|
+
const allObjs = await this.adapter.getAdapterObjectsAsync();
|
|
123
|
+
const keys = Object.keys(allObjs);
|
|
124
|
+
let removed = 0;
|
|
125
|
+
|
|
126
|
+
for (const id of keys) {
|
|
127
|
+
if (
|
|
128
|
+
(id.startsWith('analytics.statistics.temperature.week.') ||
|
|
129
|
+
id.startsWith('analytics.statistics.temperature.month.')) &&
|
|
130
|
+
id.endsWith('.reset_today')
|
|
131
|
+
) {
|
|
132
|
+
try {
|
|
133
|
+
// Erst Statewert entfernen
|
|
134
|
+
await this.adapter.delStateAsync(id);
|
|
135
|
+
} catch {
|
|
136
|
+
this.adapter.log.debug(`[migrationHelper] Kein Statewert für ${id} vorhanden (Überspringe).`);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Danach Objekt löschen (auch wenn persist=true)
|
|
140
|
+
try {
|
|
141
|
+
await this.adapter.delObjectAsync(id, { recursive: false });
|
|
142
|
+
this.adapter.log.info(`[migrationHelper] Veralteter Reset-Button entfernt: ${id}`);
|
|
143
|
+
removed++;
|
|
144
|
+
} catch (err) {
|
|
145
|
+
this.adapter.log.warn(`[migrationHelper] Konnte ${id} nicht löschen: ${err.message}`);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (removed === 0) {
|
|
151
|
+
this.adapter.log.debug('[migrationHelper] Keine alten Reset-Buttons gefunden.');
|
|
152
|
+
} else {
|
|
153
|
+
this.adapter.log.info(`[migrationHelper] Insgesamt ${removed} alte Reset-Buttons entfernt.`);
|
|
154
|
+
}
|
|
155
|
+
} catch (err) {
|
|
156
|
+
this.adapter.log.warn(`[migrationHelper] Fehler beim Entfernen alter Reset-Buttons: ${err.message}`);
|
|
157
|
+
}
|
|
158
|
+
},
|
|
149
159
|
};
|
|
150
160
|
|
|
151
161
|
module.exports = migrationHelper;
|
|
@@ -152,18 +152,29 @@ const photovoltaicHelper = {
|
|
|
152
152
|
return this._maybeStopPump(false, 0, 'mode_not_auto_pv');
|
|
153
153
|
}
|
|
154
154
|
|
|
155
|
-
//
|
|
155
|
+
// FIX: PV-Helfer darf nur aktiv sein, solange Umwälzung noch nicht erfüllt ist
|
|
156
156
|
if (ignoreOnCirc) {
|
|
157
157
|
try {
|
|
158
158
|
const remainingState = await this.adapter.getForeignStateAsync(
|
|
159
159
|
'poolcontrol.0.circulation.daily_remaining',
|
|
160
160
|
);
|
|
161
161
|
const remaining = Number(remainingState?.val ?? NaN);
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
)
|
|
166
|
-
|
|
162
|
+
|
|
163
|
+
if (Number.isFinite(remaining)) {
|
|
164
|
+
// RULE: Wenn Umwälzung bereits erfüllt → Pumpe sofort AUS (ohne Nachlauf)
|
|
165
|
+
if (remaining <= 0) {
|
|
166
|
+
this.adapter.log.info(
|
|
167
|
+
`[photovoltaicHelper] Tagesumwälzung erreicht (daily_remaining=${remaining}) → PV-Steuerung beendet, Pumpe AUS.`,
|
|
168
|
+
);
|
|
169
|
+
return this._maybeStopPump(true, 0, 'circulation_reached_force_off');
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// RULE: Wenn Umwälzung noch nicht erfüllt → nur dann darf bei Überschuss eingeschaltet werden
|
|
173
|
+
if (remaining > 0 && surplusActive) {
|
|
174
|
+
this.adapter.log.debug(
|
|
175
|
+
`[photovoltaicHelper] Tagesumwälzung noch nicht erfüllt (${remaining}) → PV-Steuerung aktiv.`,
|
|
176
|
+
);
|
|
177
|
+
}
|
|
167
178
|
}
|
|
168
179
|
} catch (err) {
|
|
169
180
|
this.adapter.log.debug(
|
|
@@ -172,10 +183,31 @@ const photovoltaicHelper = {
|
|
|
172
183
|
}
|
|
173
184
|
}
|
|
174
185
|
|
|
175
|
-
// Schaltlogik
|
|
186
|
+
// FIX: PV-Schaltlogik mit Prüfung der Umwälzung
|
|
176
187
|
if (surplusActive) {
|
|
177
|
-
|
|
188
|
+
try {
|
|
189
|
+
const remainingState = await this.adapter.getForeignStateAsync(
|
|
190
|
+
'poolcontrol.0.circulation.daily_remaining',
|
|
191
|
+
);
|
|
192
|
+
const remaining = Number(remainingState?.val ?? NaN);
|
|
193
|
+
|
|
194
|
+
// RULE: Einschalten nur, wenn Umwälzung noch nicht erfüllt
|
|
195
|
+
if (Number.isFinite(remaining) && remaining <= 0) {
|
|
196
|
+
this.adapter.log.info(
|
|
197
|
+
`[photovoltaicHelper] Tagesumwälzung bereits erfüllt (${remaining}) → Pumpe bleibt AUS (kein Start trotz Überschuss).`,
|
|
198
|
+
);
|
|
199
|
+
return this._maybeStopPump(true, 0, 'circulation_already_reached');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// RULE: Überschuss aktiv UND Umwälzung noch nicht erfüllt → einschalten
|
|
203
|
+
return this._maybeStartPump('pv_surplus');
|
|
204
|
+
} catch (err) {
|
|
205
|
+
this.adapter.log.warn(`[photovoltaicHelper] Fehler beim Prüfen von daily_remaining: ${err.message}`);
|
|
206
|
+
return this._maybeStartPump('pv_surplus');
|
|
207
|
+
}
|
|
178
208
|
}
|
|
209
|
+
|
|
210
|
+
// RULE: Kein Überschuss → ggf. Nachlauf/Aus
|
|
179
211
|
return this._maybeStopPump(false, afterrunMin, 'pv_ended_afterrun');
|
|
180
212
|
},
|
|
181
213
|
|
|
@@ -10,6 +10,7 @@ const speechHelper = {
|
|
|
10
10
|
adapter: null,
|
|
11
11
|
lastTempNotify: {}, // Cooldown-Speicher pro Sensor
|
|
12
12
|
lastPumpState: null, // interner Speicher für letzten Pumpenzustand
|
|
13
|
+
quietTime: {}, // 🆕 Cache für Alexa-Ruhezeiten
|
|
13
14
|
|
|
14
15
|
init(adapter) {
|
|
15
16
|
this.adapter = adapter;
|
|
@@ -22,7 +23,10 @@ const speechHelper = {
|
|
|
22
23
|
this.adapter.subscribeStates('temperature.*.current'); // Temp-Trigger
|
|
23
24
|
this.adapter.subscribeStates('pump.pump_switch'); // wichtig für Flankenerkennung
|
|
24
25
|
this.adapter.subscribeStates('speech.last_text');
|
|
25
|
-
this.adapter.subscribeStates('speech.queue'); //
|
|
26
|
+
this.adapter.subscribeStates('speech.queue'); // zentrale Nachrichtenwarteschlange
|
|
27
|
+
|
|
28
|
+
// 🆕 Alexa-Ruhezeit-States abonnieren
|
|
29
|
+
this.adapter.subscribeStates('speech.amazon_alexa.*');
|
|
26
30
|
|
|
27
31
|
this.adapter.log.debug('[speechHelper] initialisiert');
|
|
28
32
|
},
|
|
@@ -32,6 +36,13 @@ const speechHelper = {
|
|
|
32
36
|
return;
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
// 🆕 Wenn sich ein Alexa-Ruhezeit-State geändert hat, Werte zwischenspeichern
|
|
40
|
+
if (id.startsWith('poolcontrol.0.speech.amazon_alexa.')) {
|
|
41
|
+
const key = id.split('.').pop();
|
|
42
|
+
this.quietTime[key] = state.val;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
35
46
|
// Globale Aktivierung prüfen
|
|
36
47
|
const active = (await this.adapter.getStateAsync('speech.active'))?.val;
|
|
37
48
|
if (!active) {
|
|
@@ -66,35 +77,6 @@ const speechHelper = {
|
|
|
66
77
|
return;
|
|
67
78
|
}
|
|
68
79
|
|
|
69
|
-
/*
|
|
70
|
-
*
|
|
71
|
-
* Deaktiviert, ersetzt durch speechTextHelper
|
|
72
|
-
*
|
|
73
|
-
* // === Pumpenstart / -stop nur bei Zustandswechsel ===
|
|
74
|
-
* if (id.endsWith('pump.pump_switch')) {
|
|
75
|
-
* const newVal = !!state.val;
|
|
76
|
-
*
|
|
77
|
-
* // Nur wenn sich der Zustand wirklich geändert hat
|
|
78
|
-
* if (this.lastPumpState !== newVal) {
|
|
79
|
-
* this.lastPumpState = newVal;
|
|
80
|
-
*
|
|
81
|
-
* if (newVal) {
|
|
82
|
-
* const txt =
|
|
83
|
-
* (await this.adapter.getStateAsync('speech.start_text'))?.val ||
|
|
84
|
-
* 'Die Poolpumpe wurde gestartet.';
|
|
85
|
-
* await this._speak(txt);
|
|
86
|
-
* } else {
|
|
87
|
-
* const txt =
|
|
88
|
-
* (await this.adapter.getStateAsync('speech.end_text'))?.val || 'Die Poolpumpe wurde gestoppt.';
|
|
89
|
-
* await this._speak(txt);
|
|
90
|
-
* }
|
|
91
|
-
* } else {
|
|
92
|
-
* this.adapter.log.debug('[speechHelper] Ignoriere Pumpenmeldung – kein Zustandswechsel.');
|
|
93
|
-
* }
|
|
94
|
-
* return;
|
|
95
|
-
* }
|
|
96
|
-
*/
|
|
97
|
-
|
|
98
80
|
// Nur Pool-Oberflächentemperatur berücksichtigen
|
|
99
81
|
if (!id.includes('temperature.surface')) {
|
|
100
82
|
return;
|
|
@@ -129,6 +111,64 @@ const speechHelper = {
|
|
|
129
111
|
return;
|
|
130
112
|
},
|
|
131
113
|
|
|
114
|
+
// 🆕 Prüft, ob Alexa aktuell sprechen darf
|
|
115
|
+
async _isAlexaAllowed() {
|
|
116
|
+
try {
|
|
117
|
+
const now = new Date();
|
|
118
|
+
const currentDay = now.getDay(); // 0=So, 6=Sa
|
|
119
|
+
const isWeekend = currentDay === 0 || currentDay === 6;
|
|
120
|
+
const hhmm = now.toTimeString().slice(0, 5);
|
|
121
|
+
|
|
122
|
+
// Werte aus Cache oder States holen
|
|
123
|
+
const prefix = 'speech.amazon_alexa.';
|
|
124
|
+
const enabledState = isWeekend ? 'quiet_time_weekend_enabled' : 'quiet_time_week_enabled';
|
|
125
|
+
const startState = isWeekend ? 'quiet_time_weekend_start' : 'quiet_time_week_start';
|
|
126
|
+
const endState = isWeekend ? 'quiet_time_weekend_end' : 'quiet_time_week_end';
|
|
127
|
+
|
|
128
|
+
const enabled =
|
|
129
|
+
this.quietTime[enabledState] ?? (await this.adapter.getStateAsync(prefix + enabledState))?.val ?? false;
|
|
130
|
+
const start =
|
|
131
|
+
this.quietTime[startState] ?? (await this.adapter.getStateAsync(prefix + startState))?.val ?? '22:00';
|
|
132
|
+
const end =
|
|
133
|
+
this.quietTime[endState] ?? (await this.adapter.getStateAsync(prefix + endState))?.val ?? '07:00';
|
|
134
|
+
|
|
135
|
+
if (!enabled) {
|
|
136
|
+
await this.adapter.setStateAsync(`${prefix}quiet_time_active_now`, { val: false, ack: true });
|
|
137
|
+
return true;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Zeitvergleich
|
|
141
|
+
const inRange = this._isTimeInRange(hhmm, start, end);
|
|
142
|
+
await this.adapter.setStateAsync(`${prefix}quiet_time_active_now`, { val: inRange, ack: true });
|
|
143
|
+
|
|
144
|
+
if (inRange) {
|
|
145
|
+
this.adapter.log.debug('[speechHelper] Alexa-Ruhezeit aktiv – Sprachausgabe blockiert.');
|
|
146
|
+
return false;
|
|
147
|
+
}
|
|
148
|
+
return true;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
this.adapter.log.warn(`[speechHelper] Fehler bei Alexa-Ruhezeitprüfung: ${err.message}`);
|
|
151
|
+
return true; // im Zweifel sprechen lassen
|
|
152
|
+
}
|
|
153
|
+
},
|
|
154
|
+
|
|
155
|
+
// 🆕 Hilfsfunktion zum Zeitvergleich
|
|
156
|
+
_isTimeInRange(now, start, end) {
|
|
157
|
+
// Umwandeln in Minuten
|
|
158
|
+
const toMinutes = t => {
|
|
159
|
+
const [h, m] = t.split(':').map(Number);
|
|
160
|
+
return h * 60 + m;
|
|
161
|
+
};
|
|
162
|
+
const n = toMinutes(now);
|
|
163
|
+
const s = toMinutes(start);
|
|
164
|
+
const e = toMinutes(end);
|
|
165
|
+
|
|
166
|
+
if (s < e) {
|
|
167
|
+
return n >= s && n < e;
|
|
168
|
+
}
|
|
169
|
+
return n >= s || n < e; // über Mitternacht
|
|
170
|
+
},
|
|
171
|
+
|
|
132
172
|
async _speak(text) {
|
|
133
173
|
try {
|
|
134
174
|
if (!text) {
|
|
@@ -138,29 +178,31 @@ const speechHelper = {
|
|
|
138
178
|
// Letzten Text speichern
|
|
139
179
|
await this.adapter.setStateAsync('speech.last_text', { val: text, ack: true });
|
|
140
180
|
|
|
141
|
-
// Alexa-Ausgabe
|
|
181
|
+
// 🆕 Alexa-Ausgabe mit Ruhezeitprüfung
|
|
142
182
|
if (this.adapter.config.speech_alexa_enabled && this.adapter.config.speech_alexa_device) {
|
|
143
|
-
await this.
|
|
144
|
-
|
|
183
|
+
const allowed = await this._isAlexaAllowed();
|
|
184
|
+
if (allowed) {
|
|
185
|
+
await this.adapter.setForeignStateAsync(this.adapter.config.speech_alexa_device, text);
|
|
186
|
+
this.adapter.log.info(`[speechHelper] Alexa sagt: ${text}`);
|
|
187
|
+
} else {
|
|
188
|
+
this.adapter.log.debug('[speechHelper] Alexa stumm (Ruhezeit aktiv).');
|
|
189
|
+
}
|
|
145
190
|
}
|
|
146
191
|
|
|
147
192
|
// Telegram-Ausgabe (modern über sendTo)
|
|
148
193
|
if (this.adapter.config.speech_telegram_enabled && this.adapter.config.speech_telegram_instance) {
|
|
149
194
|
const instance = this.adapter.config.speech_telegram_instance;
|
|
150
195
|
try {
|
|
151
|
-
// NEU: Benutzerliste aus Admin lesen (Komma-getrennte Namen)
|
|
152
196
|
const rawUsers = this.adapter.config.speech_telegram_users || '';
|
|
153
197
|
const users = rawUsers
|
|
154
198
|
.split(',')
|
|
155
199
|
.map(u => u.trim())
|
|
156
200
|
.filter(Boolean);
|
|
157
201
|
|
|
158
|
-
// Wenn keine Benutzer angegeben → Standard: global an alle senden
|
|
159
202
|
if (users.length === 0) {
|
|
160
203
|
await this.adapter.sendToAsync(instance, { text, parse_mode: 'Markdown' });
|
|
161
204
|
this.adapter.log.info(`[speechHelper] Telegram (global): ${text}`);
|
|
162
205
|
} else {
|
|
163
|
-
// Nur an ausgewählte Benutzer senden
|
|
164
206
|
for (const user of users) {
|
|
165
207
|
await this.adapter.sendToAsync(instance, { user, text, parse_mode: 'Markdown' });
|
|
166
208
|
this.adapter.log.info(`[speechHelper] Telegram an ${user}: ${text}`);
|
|
@@ -175,7 +217,7 @@ const speechHelper = {
|
|
|
175
217
|
|
|
176
218
|
// E-Mail-Ausgabe (modern über sendTo)
|
|
177
219
|
if (this.adapter.config.speech_email_enabled && this.adapter.config.speech_email_instance) {
|
|
178
|
-
const instance = this.adapter.config.speech_email_instance;
|
|
220
|
+
const instance = this.adapter.config.speech_email_instance;
|
|
179
221
|
try {
|
|
180
222
|
this.adapter.sendTo(instance, {
|
|
181
223
|
to: this.adapter.config.speech_email_recipient,
|
|
@@ -195,7 +237,9 @@ const speechHelper = {
|
|
|
195
237
|
},
|
|
196
238
|
|
|
197
239
|
cleanup() {
|
|
198
|
-
|
|
240
|
+
if (this.adapter) {
|
|
241
|
+
this.adapter.log.debug('[speechHelper] Cleanup abgeschlossen.');
|
|
242
|
+
}
|
|
199
243
|
},
|
|
200
244
|
};
|
|
201
245
|
|
|
@@ -250,6 +250,12 @@ const statisticsHelperMonth = {
|
|
|
250
250
|
});
|
|
251
251
|
|
|
252
252
|
await this._updateOverallSummary();
|
|
253
|
+
|
|
254
|
+
// FIX: last_update korrigieren (echtes Aktualisierungsdatum)
|
|
255
|
+
await adapter.setStateAsync(`${basePath}.last_update`, {
|
|
256
|
+
val: new Date().toISOString(),
|
|
257
|
+
ack: true,
|
|
258
|
+
});
|
|
253
259
|
},
|
|
254
260
|
|
|
255
261
|
/**
|
|
@@ -385,6 +391,20 @@ const statisticsHelperMonth = {
|
|
|
385
391
|
async _resetMonthlyTemperatureStats() {
|
|
386
392
|
const adapter = this.adapter;
|
|
387
393
|
|
|
394
|
+
// 🟢 Neuer Logikblock: Monatsreset erfolgt nur, wenn wirklich neuer Monat begonnen hat
|
|
395
|
+
try {
|
|
396
|
+
const now = new Date();
|
|
397
|
+
const currentDay = now.getDate();
|
|
398
|
+
|
|
399
|
+
// Nur am 1. Tag des Monats ausführen (00:05 Uhr geplant)
|
|
400
|
+
if (currentDay !== 1) {
|
|
401
|
+
adapter.log.debug('[statisticsHelperMonth] Kein Monatsanfang – Reset übersprungen.');
|
|
402
|
+
return;
|
|
403
|
+
}
|
|
404
|
+
} catch (err) {
|
|
405
|
+
adapter.log.warn(`[statisticsHelperMonth] Fehler bei Monatsbeginn-Prüfung: ${err.message}`);
|
|
406
|
+
}
|
|
407
|
+
|
|
388
408
|
// 🟢 NEU: Schutz vor Endlosschleifen und Mehrfachausführung
|
|
389
409
|
if (this.isResetting) {
|
|
390
410
|
adapter.log.debug('statisticsHelperMonth: Reset bereits aktiv – übersprungen.');
|
|
@@ -104,6 +104,93 @@ async function createSpeechStates(adapter) {
|
|
|
104
104
|
ack: true,
|
|
105
105
|
});
|
|
106
106
|
|
|
107
|
+
// ------------------------------------------------------------------
|
|
108
|
+
// 🆕 Amazon Alexa – Ruhezeiten
|
|
109
|
+
// ------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
await adapter.setObjectNotExistsAsync('speech.amazon_alexa', {
|
|
112
|
+
type: 'channel',
|
|
113
|
+
common: { name: 'Amazon Alexa (Sprachausgabe)' },
|
|
114
|
+
native: {},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
const alexaStates = [
|
|
118
|
+
{
|
|
119
|
+
id: 'quiet_time_week_enabled',
|
|
120
|
+
name: 'Ruhezeit (Mo–Fr) aktiv',
|
|
121
|
+
type: 'boolean',
|
|
122
|
+
role: 'switch',
|
|
123
|
+
def: false,
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
id: 'quiet_time_week_start',
|
|
127
|
+
name: 'Ruhezeit (Mo–Fr) Startzeit (HH:MM)',
|
|
128
|
+
type: 'string',
|
|
129
|
+
role: 'value.time',
|
|
130
|
+
def: '22:00',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'quiet_time_week_end',
|
|
134
|
+
name: 'Ruhezeit (Mo–Fr) Endzeit (HH:MM)',
|
|
135
|
+
type: 'string',
|
|
136
|
+
role: 'value.time',
|
|
137
|
+
def: '07:00',
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
id: 'quiet_time_weekend_enabled',
|
|
141
|
+
name: 'Ruhezeit (Sa–So) aktiv',
|
|
142
|
+
type: 'boolean',
|
|
143
|
+
role: 'switch',
|
|
144
|
+
def: false,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
id: 'quiet_time_weekend_start',
|
|
148
|
+
name: 'Ruhezeit (Sa–So) Startzeit (HH:MM)',
|
|
149
|
+
type: 'string',
|
|
150
|
+
role: 'value.time',
|
|
151
|
+
def: '22:00',
|
|
152
|
+
},
|
|
153
|
+
{
|
|
154
|
+
id: 'quiet_time_weekend_end',
|
|
155
|
+
name: 'Ruhezeit (Sa–So) Endzeit (HH:MM)',
|
|
156
|
+
type: 'string',
|
|
157
|
+
role: 'value.time',
|
|
158
|
+
def: '08:00',
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
id: 'quiet_time_active_now',
|
|
162
|
+
name: 'Alexa derzeit stumm (Ruhezeit aktiv)',
|
|
163
|
+
type: 'boolean',
|
|
164
|
+
role: 'indicator',
|
|
165
|
+
def: false,
|
|
166
|
+
write: false,
|
|
167
|
+
},
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
for (const s of alexaStates) {
|
|
171
|
+
const fullId = `speech.amazon_alexa.${s.id}`;
|
|
172
|
+
await adapter.setObjectNotExistsAsync(fullId, {
|
|
173
|
+
type: 'state',
|
|
174
|
+
common: {
|
|
175
|
+
name: s.name,
|
|
176
|
+
type: s.type,
|
|
177
|
+
role: s.role,
|
|
178
|
+
read: true,
|
|
179
|
+
write: Boolean(s.write !== false), // Standard: true
|
|
180
|
+
def: s.def,
|
|
181
|
+
persist: true,
|
|
182
|
+
},
|
|
183
|
+
native: {},
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
const current = await adapter.getStateAsync(fullId);
|
|
187
|
+
if (!current || current.val === null || current.val === undefined) {
|
|
188
|
+
await adapter.setStateAsync(fullId, { val: s.def, ack: true });
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
adapter.log.debug('[speechStates] Amazon Alexa – Ruhezeit-States geprüft und angelegt.');
|
|
193
|
+
|
|
107
194
|
// versteckte Dateien
|
|
108
195
|
|
|
109
196
|
await adapter.setObjectNotExistsAsync('speech.solar_active', {
|
|
@@ -100,7 +100,6 @@ async function _createTemperatureStatsGroup(adapter, periodId, displayName) {
|
|
|
100
100
|
{ id: 'temp_avg', name: 'Durchschnittstemperatur', type: 'number', role: 'value.temperature', unit: '°C' },
|
|
101
101
|
{ id: 'data_points_count', name: 'Anzahl Messwerte', type: 'number', role: 'value' },
|
|
102
102
|
{ id: 'last_update', name: 'Letzte Aktualisierung', type: 'string', role: 'value.time' },
|
|
103
|
-
{ id: 'reset_today', name: 'Tagesstatistik zurücksetzen', type: 'boolean', role: 'button' },
|
|
104
103
|
{
|
|
105
104
|
id: 'summary_json',
|
|
106
105
|
name: `${displayName} (JSON)`,
|
|
@@ -115,6 +114,16 @@ async function _createTemperatureStatsGroup(adapter, periodId, displayName) {
|
|
|
115
114
|
},
|
|
116
115
|
];
|
|
117
116
|
|
|
117
|
+
// FIX: Reset-Button nur bei Tagesstatistik anlegen
|
|
118
|
+
if (periodId === 'today') {
|
|
119
|
+
stateDefs.push({
|
|
120
|
+
id: 'reset_today',
|
|
121
|
+
name: 'Tagesstatistik zurücksetzen',
|
|
122
|
+
type: 'boolean',
|
|
123
|
+
role: 'button',
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
118
127
|
for (const def of stateDefs) {
|
|
119
128
|
await adapter.setObjectNotExistsAsync(`${basePath}.${def.id}`, {
|
|
120
129
|
type: 'state',
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "iobroker.poolcontrol",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
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",
|