iobroker.poolcontrol 0.1.1 → 0.2.0

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.
@@ -0,0 +1,184 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * controlStates
5
+ * - Erstellt States, die zur Laufzeit über VIS oder Blockly steuerbar sind
6
+ * - Erstellt zusätzlich beschriftete Channel-Struktur:
7
+ * control
8
+ * └─ season
9
+ * └─ active
10
+ */
11
+
12
+ /**
13
+ * Erstellt alle States im control-Bereich.
14
+ *
15
+ * @param {import('iobroker').Adapter} adapter - ioBroker Adapterinstanz
16
+ */
17
+ async function createControlStates(adapter) {
18
+ try {
19
+ // Channel: control
20
+ await adapter.setObjectNotExistsAsync('control', {
21
+ type: 'channel',
22
+ common: {
23
+ name: 'Steuerung',
24
+ desc: 'Manuelle Steuerung und Laufzeitfunktionen (VIS / Blockly)',
25
+ },
26
+ native: {},
27
+ });
28
+
29
+ // Channel: control.season
30
+ await adapter.setObjectNotExistsAsync('control.season', {
31
+ type: 'channel',
32
+ common: {
33
+ name: 'Saisonsteuerung',
34
+ desc: 'Steuerung der aktiven Poolsaison und saisonabhängiger Funktionen',
35
+ },
36
+ native: {},
37
+ });
38
+
39
+ // State: control.season.active
40
+ await adapter.setObjectNotExistsAsync('control.season.active', {
41
+ type: 'state',
42
+ common: {
43
+ name: 'Poolsaison aktiv',
44
+ desc: 'Zeigt an, ob die Poolsaison aktiv ist (steuerbar über VIS oder Blockly)',
45
+ type: 'boolean',
46
+ role: 'switch',
47
+ read: true,
48
+ write: true,
49
+ def: false,
50
+ },
51
+ native: {},
52
+ });
53
+
54
+ // Initialwert aus der Instanzkonfiguration übernehmen
55
+ const cfgValue = !!adapter.config.season_active;
56
+ await adapter.setStateAsync('control.season.active', { val: cfgValue, ack: true });
57
+
58
+ adapter.log.debug(`[controlStates] State control.season.active initialisiert mit Wert: ${cfgValue}`);
59
+
60
+ // ---------------------------------------------------------------------
61
+ // Channel: control.pump
62
+ await adapter.setObjectNotExistsAsync('control.pump', {
63
+ type: 'channel',
64
+ common: {
65
+ name: 'Pumpensteuerung',
66
+ desc: 'Manuelle Aktionen wie Rückspülen oder Wartung',
67
+ },
68
+ native: {},
69
+ });
70
+
71
+ // Rückspülung starten
72
+ await adapter.setObjectNotExistsAsync('control.pump.backwash_start', {
73
+ type: 'state',
74
+ common: {
75
+ name: 'Rückspülung starten',
76
+ desc: 'Startet die Rückspülung für die eingestellte Dauer',
77
+ type: 'boolean',
78
+ role: 'button',
79
+ read: true,
80
+ write: true,
81
+ def: false,
82
+ },
83
+ native: {},
84
+ });
85
+ await adapter.setStateAsync('control.pump.backwash_start', { val: false, ack: true });
86
+
87
+ // Rückspülung aktiv
88
+ await adapter.setObjectNotExistsAsync('control.pump.backwash_active', {
89
+ type: 'state',
90
+ common: {
91
+ name: 'Rückspülung aktiv',
92
+ desc: 'Zeigt an, ob gerade eine Rückspülung läuft',
93
+ type: 'boolean',
94
+ role: 'indicator',
95
+ read: true,
96
+ write: false,
97
+ def: false,
98
+ },
99
+ native: {},
100
+ });
101
+ await adapter.setStateAsync('control.pump.backwash_active', { val: false, ack: true });
102
+
103
+ // Rückspülungsdauer
104
+ await adapter.setObjectNotExistsAsync('control.pump.backwash_duration', {
105
+ type: 'state',
106
+ common: {
107
+ name: 'Dauer der Rückspülung (Minuten)',
108
+ desc: 'Bestimmt, wie lange die Rückspülung laufen soll',
109
+ type: 'number',
110
+ role: 'level.timer',
111
+ read: true,
112
+ write: true,
113
+ def: 1,
114
+ min: 1,
115
+ max: 60,
116
+ },
117
+ native: {},
118
+ });
119
+ await adapter.setStateAsync('control.pump.backwash_duration', { val: 1, ack: true });
120
+
121
+ // Wartungsmodus aktiv
122
+ await adapter.setObjectNotExistsAsync('control.pump.maintenance_active', {
123
+ type: 'state',
124
+ common: {
125
+ name: 'Wartungsmodus aktiv',
126
+ desc: 'Deaktiviert Automatikfunktionen und lässt Pumpe manuell steuern',
127
+ type: 'boolean',
128
+ role: 'switch',
129
+ read: true,
130
+ write: true,
131
+ def: false,
132
+ },
133
+ native: {},
134
+ });
135
+ await adapter.setStateAsync('control.pump.maintenance_active', { val: false, ack: true });
136
+
137
+ // Benachrichtigungen aktivieren
138
+ await adapter.setObjectNotExistsAsync('control.pump.notifications_enabled', {
139
+ type: 'state',
140
+ common: {
141
+ name: 'Benachrichtigungen aktivieren',
142
+ desc: 'Wenn aktiviert, werden bei Rückspülung und Wartung Ansagen oder Nachrichten gesendet (E-Mail, Telegram, Alexa)',
143
+ type: 'boolean',
144
+ role: 'switch',
145
+ read: true,
146
+ write: true,
147
+ def: true,
148
+ },
149
+ native: {},
150
+ });
151
+ await adapter.setStateAsync('control.pump.notifications_enabled', { val: true, ack: true });
152
+
153
+ // ---------------------------------------------------------------------
154
+ // Channel: control.energy
155
+ await adapter.setObjectNotExistsAsync('control.energy', {
156
+ type: 'channel',
157
+ common: {
158
+ name: 'Energieverwaltung',
159
+ desc: 'Funktionen zur Verbrauchs- und Kostenrücksetzung',
160
+ },
161
+ native: {},
162
+ });
163
+
164
+ // Button: Energiezähler zurücksetzen
165
+ await adapter.setObjectNotExistsAsync('control.energy.reset', {
166
+ type: 'state',
167
+ common: {
168
+ name: 'Energiezähler zurücksetzen',
169
+ desc: 'Setzt alle Verbrauchs- und Kostenwerte auf 0 (Totalreset)',
170
+ type: 'boolean',
171
+ role: 'button',
172
+ read: true,
173
+ write: true,
174
+ def: false,
175
+ },
176
+ native: {},
177
+ });
178
+ await adapter.setStateAsync('control.energy.reset', { val: false, ack: true });
179
+ } catch (err) {
180
+ adapter.log.error(`[controlStates] Fehler beim Erstellen der Control-States: ${err.message}`);
181
+ }
182
+ }
183
+
184
+ module.exports = { createControlStates };
@@ -0,0 +1,124 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Legt alle States für den Diagnosebereich an:
5
+ * - SystemCheck.debug_logs.target_area → überwachten Bereich auswählen
6
+ * - SystemCheck.debug_logs.log → fortlaufendes Log (Text)
7
+ * - SystemCheck.debug_logs.clear → löscht das Log
8
+ *
9
+ * @param {import('iobroker').Adapter} adapter - ioBroker Adapter-Instanz
10
+ */
11
+ async function createDebugLogStates(adapter) {
12
+ try {
13
+ // Oberordner SystemCheck (alphabetisch ganz unten)
14
+ await adapter.setObjectNotExistsAsync('SystemCheck', {
15
+ type: 'channel',
16
+ common: {
17
+ name: 'SystemCheck (Diagnose und Tools)',
18
+ desc: 'Enthält interne Diagnose- und Protokollierungsfunktionen des Adapters.',
19
+ },
20
+ native: {},
21
+ });
22
+
23
+ // Unterkanal für Debug-Logs
24
+ await adapter.setObjectNotExistsAsync('SystemCheck.debug_logs', {
25
+ type: 'channel',
26
+ common: {
27
+ name: 'Debug-Logs (Test & Diagnose)',
28
+ desc: 'Protokolliert auffällige oder häufige Zustandsänderungen innerhalb der Instanz.',
29
+ },
30
+ native: {},
31
+ });
32
+
33
+ // --- Dynamischer Scan aller Objekte der Instanz ---
34
+ const objects = await adapter.getObjectListAsync({
35
+ startkey: `${adapter.namespace}.`,
36
+ endkey: `${adapter.namespace}.\u9999`,
37
+ });
38
+
39
+ const areas = new Set();
40
+
41
+ for (const row of objects.rows) {
42
+ const idParts = row.id.split('.');
43
+ // Beispiel: poolcontrol.0.control.backwash.active
44
+ if (idParts.length >= 3) {
45
+ const base = idParts[2];
46
+ const sub = idParts[3];
47
+
48
+ // Nur Bereiche, keine States
49
+ // (also keine Objekte mit mehr als 4 Punkten oder zz_)
50
+ if (!base.startsWith('zz_')) {
51
+ // Hauptbereiche immer aufnehmen
52
+ areas.add(base);
53
+
54
+ // Unterbereiche nur, wenn sie selbst wieder Unterpunkte haben
55
+ // -> prüft, ob es States gibt, die mit base.sub beginnen
56
+ const hasSubEntries = objects.rows.some(r =>
57
+ r.id.startsWith(`${adapter.namespace}.${base}.${sub}.`),
58
+ );
59
+ if (hasSubEntries && sub && !sub.startsWith('zz_')) {
60
+ areas.add(`${base}.${sub}`);
61
+ }
62
+ }
63
+ }
64
+ }
65
+
66
+ // Alphabetisch sortieren und "none" hinzufügen
67
+ const availableAreas = ['none', ...Array.from(areas).sort()];
68
+
69
+ // Bereichsauswahl (target_area)
70
+ await adapter.setObjectNotExistsAsync('SystemCheck.debug_logs.target_area', {
71
+ type: 'state',
72
+ common: {
73
+ name: 'Überwachungsbereich',
74
+ desc: 'Wähle, welcher Bereich der Instanz überwacht werden soll (z. B. pump, solar, control.backwash).',
75
+ type: 'string',
76
+ role: 'text',
77
+ read: true,
78
+ write: true,
79
+ states: availableAreas.reduce((obj, v) => ((obj[v] = v), obj), {}),
80
+ def: 'none',
81
+ },
82
+ native: {},
83
+ });
84
+ await adapter.setStateAsync('SystemCheck.debug_logs.target_area', { val: 'none', ack: true });
85
+
86
+ // Fortlaufendes Log
87
+ await adapter.setObjectNotExistsAsync('SystemCheck.debug_logs.log', {
88
+ type: 'state',
89
+ common: {
90
+ name: 'Debug-Logtext',
91
+ desc: 'Fortlaufendes Protokoll des ausgewählten Bereichs.',
92
+ type: 'string',
93
+ role: 'text',
94
+ read: true,
95
+ write: false,
96
+ },
97
+ native: {},
98
+ });
99
+ await adapter.setStateAsync('SystemCheck.debug_logs.log', { val: '', ack: true });
100
+
101
+ // Clear-Button
102
+ await adapter.setObjectNotExistsAsync('SystemCheck.debug_logs.clear', {
103
+ type: 'state',
104
+ common: {
105
+ name: 'Log löschen',
106
+ desc: 'Löscht das fortlaufende Log im Kanal SystemCheck.debug_logs.',
107
+ type: 'boolean',
108
+ role: 'button',
109
+ read: true,
110
+ write: true,
111
+ def: false,
112
+ },
113
+ native: {},
114
+ });
115
+ await adapter.setStateAsync('SystemCheck.debug_logs.clear', { val: false, ack: true });
116
+
117
+ adapter.log.debug(`[debugLogStates] Debug-Log-States erfolgreich angelegt`);
118
+ adapter.log.debug(`[debugLogStates] Verfügbare Bereiche: ${availableAreas.join(', ')}`);
119
+ } catch (err) {
120
+ adapter.log.error(`[debugLogStates] Fehler beim Erstellen der States: ${err.message}`);
121
+ }
122
+ }
123
+
124
+ module.exports = { createDebugLogStates };
@@ -4,7 +4,9 @@
4
4
  * Legt alle States für Laufzeit- und Umwälzwerte an:
5
5
  * - runtime.total
6
6
  * - runtime.today
7
- * - runtime.formatted
7
+ * - runtime.start_count_today
8
+ * - runtime.current_session
9
+ * - runtime.season_total
8
10
  * - circulation.daily_total
9
11
  * - circulation.daily_required
10
12
  * - circulation.daily_remaining
@@ -21,38 +23,72 @@ async function createRuntimeStates(adapter) {
21
23
  native: {},
22
24
  });
23
25
 
26
+ // Gesamtlaufzeit (formatiert)
24
27
  await adapter.setObjectNotExistsAsync('runtime.total', {
25
28
  type: 'state',
26
29
  common: {
27
- name: 'Gesamtlaufzeit',
28
- type: 'number',
29
- role: 'value.time',
30
- unit: 's',
30
+ name: 'Gesamtlaufzeit (formatiert)',
31
+ type: 'string',
32
+ role: 'text',
31
33
  read: true,
32
34
  write: false,
33
35
  persist: true,
34
36
  },
35
37
  native: {},
36
38
  });
39
+ await adapter.setStateAsync('runtime.total', { val: '0h 0m 0s', ack: true });
37
40
 
41
+ // Tageslaufzeit (formatiert)
38
42
  await adapter.setObjectNotExistsAsync('runtime.today', {
39
43
  type: 'state',
40
44
  common: {
41
- name: 'Tageslaufzeit',
45
+ name: 'Tageslaufzeit (formatiert)',
46
+ type: 'string',
47
+ role: 'text',
48
+ read: true,
49
+ write: false,
50
+ persist: true,
51
+ },
52
+ native: {},
53
+ });
54
+ await adapter.setStateAsync('runtime.today', { val: '0h 0m 0s', ack: true });
55
+
56
+ // -------------------------------------------------------------------------
57
+ // NEU: Pumpenstarts heute
58
+ await adapter.setObjectNotExistsAsync('runtime.start_count_today', {
59
+ type: 'state',
60
+ common: {
61
+ name: 'Pumpenstarts heute',
42
62
  type: 'number',
43
- role: 'value.time',
44
- unit: 's',
63
+ role: 'value',
45
64
  read: true,
46
65
  write: false,
47
66
  persist: true,
48
67
  },
49
68
  native: {},
50
69
  });
70
+ await adapter.setStateAsync('runtime.start_count_today', { val: 0, ack: true });
71
+
72
+ // NEU: Aktuelle Laufzeit (seit Einschalten)
73
+ await adapter.setObjectNotExistsAsync('runtime.current_session', {
74
+ type: 'state',
75
+ common: {
76
+ name: 'Aktuelle Laufzeit (seit Einschalten)',
77
+ type: 'string',
78
+ role: 'text',
79
+ read: true,
80
+ write: false,
81
+ persist: false,
82
+ },
83
+ native: {},
84
+ });
85
+ await adapter.setStateAsync('runtime.current_session', { val: '0h 0m 0s', ack: true });
51
86
 
52
- await adapter.setObjectNotExistsAsync('runtime.formatted', {
87
+ // NEU: Gesamtlaufzeit der aktuellen Saison (formatiert)
88
+ await adapter.setObjectNotExistsAsync('runtime.season_total', {
53
89
  type: 'state',
54
90
  common: {
55
- name: 'Formatierte Laufzeit heute',
91
+ name: 'Gesamtlaufzeit aktuelle Saison (formatiert)',
56
92
  type: 'string',
57
93
  role: 'text',
58
94
  read: true,
@@ -61,7 +97,9 @@ async function createRuntimeStates(adapter) {
61
97
  },
62
98
  native: {},
63
99
  });
100
+ await adapter.setStateAsync('runtime.season_total', { val: '0h 0m 0s', ack: true });
64
101
 
102
+ // -------------------------------------------------------------------------
65
103
  // --- Kanal circulation ---
66
104
  await adapter.setObjectNotExistsAsync('circulation', {
67
105
  type: 'channel',
@@ -42,7 +42,7 @@ async function createSpeechStates(adapter) {
42
42
  type: 'string',
43
43
  role: 'text',
44
44
  read: true,
45
- write: false,
45
+ write: true,
46
46
  },
47
47
  native: {},
48
48
  });
@@ -80,23 +80,6 @@ async function createSpeechStates(adapter) {
80
80
  val: 'Die Poolpumpe wurde gestoppt.',
81
81
  ack: true,
82
82
  });
83
-
84
- // Temperaturtexte (optional)
85
- const sensors = ['surface', 'ground', 'flow', 'return', 'collector', 'outside'];
86
- for (const key of sensors) {
87
- await adapter.setObjectNotExistsAsync(`speech.texts.${key}`, {
88
- type: 'state',
89
- common: {
90
- name: `Textausgabe Sensor ${key}`,
91
- type: 'string',
92
- role: 'text',
93
- read: true,
94
- write: true,
95
- },
96
- native: {},
97
- });
98
- await adapter.setStateAsync(`speech.texts.${key}`, { val: '', ack: true });
99
- }
100
83
  }
101
84
 
102
85
  module.exports = {
package/main.js CHANGED
@@ -14,6 +14,8 @@ const consumptionHelper = require('./lib/helpers/consumptionHelper');
14
14
  const solarHelper = require('./lib/helpers/solarHelper');
15
15
  const frostHelper = require('./lib/helpers/frostHelper');
16
16
  const statusHelper = require('./lib/helpers/statusHelper');
17
+ const controlHelper = require('./lib/helpers/controlHelper');
18
+ const debugLogHelper = require('./lib/helpers/debugLogHelper');
17
19
  const { createTemperatureStates } = require('./lib/stateDefinitions/temperatureStates');
18
20
  const { createPumpStates } = require('./lib/stateDefinitions/pumpStates');
19
21
  const { createSolarStates } = require('./lib/stateDefinitions/solarStates');
@@ -23,6 +25,8 @@ const { createRuntimeStates } = require('./lib/stateDefinitions/runtimeStates');
23
25
  const { createSpeechStates } = require('./lib/stateDefinitions/speechStates');
24
26
  const { createConsumptionStates } = require('./lib/stateDefinitions/consumptionStates');
25
27
  const { createStatusStates } = require('./lib/stateDefinitions/statusStates');
28
+ const { createControlStates } = require('./lib/stateDefinitions/controlStates');
29
+ const { createDebugLogStates } = require('./lib/stateDefinitions/debugLogStates');
26
30
 
27
31
  class Poolcontrol extends utils.Adapter {
28
32
  constructor(options) {
@@ -71,6 +75,12 @@ class Poolcontrol extends utils.Adapter {
71
75
  ack: true,
72
76
  });
73
77
 
78
+ // --- Control States ---
79
+ await createControlStates(this);
80
+
81
+ // --- DebugLog Staets ---
82
+ await createDebugLogStates(this);
83
+
74
84
  // --- Helper starten ---
75
85
  temperatureHelper.init(this);
76
86
  timeHelper.init(this);
@@ -81,6 +91,8 @@ class Poolcontrol extends utils.Adapter {
81
91
  solarHelper.init(this);
82
92
  frostHelper.init(this);
83
93
  statusHelper.init(this);
94
+ controlHelper.init(this);
95
+ debugLogHelper.init(this);
84
96
  }
85
97
 
86
98
  onUnload(callback) {
@@ -112,6 +124,9 @@ class Poolcontrol extends utils.Adapter {
112
124
  if (statusHelper.cleanup) {
113
125
  statusHelper.cleanup();
114
126
  }
127
+ if (controlHelper.cleanup) {
128
+ controlHelper.cleanup();
129
+ }
115
130
  } catch (e) {
116
131
  this.log.warn(`[onUnload] Fehler beim Cleanup: ${e.message}`);
117
132
  } finally {
@@ -163,6 +178,11 @@ class Poolcontrol extends utils.Adapter {
163
178
  } catch (e) {
164
179
  this.log.warn(`[statusHelper] Fehler in handleStateChange: ${e.message}`);
165
180
  }
181
+ if (id.includes('control.')) {
182
+ controlHelper.handleStateChange(id, state);
183
+ }
184
+
185
+ await debugLogHelper.handleStateChange(id, state);
166
186
  }
167
187
  }
168
188
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "iobroker.poolcontrol",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
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",