iobroker.poolcontrol 0.0.7
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/LICENSE +22 -0
- package/README.md +129 -0
- package/admin/i18n/de/translations.json +5 -0
- package/admin/i18n/en/translations.json +5 -0
- package/admin/i18n/es/translations.json +5 -0
- package/admin/i18n/fr/translations.json +5 -0
- package/admin/i18n/it/translations.json +5 -0
- package/admin/i18n/nl/translations.json +5 -0
- package/admin/i18n/pl/translations.json +5 -0
- package/admin/i18n/pt/translations.json +5 -0
- package/admin/i18n/ru/translations.json +5 -0
- package/admin/i18n/uk/translations.json +5 -0
- package/admin/i18n/zh-cn/translations.json +5 -0
- package/admin/jsonConfig.json +901 -0
- package/admin/poolcontrol.png +0 -0
- package/io-package.json +176 -0
- package/lib/adapter-config.d.ts +19 -0
- package/lib/helpers/consumptionHelper.js +185 -0
- package/lib/helpers/frostHelper.js +94 -0
- package/lib/helpers/pumpHelper.js +224 -0
- package/lib/helpers/runtimeHelper.js +159 -0
- package/lib/helpers/solarHelper.js +138 -0
- package/lib/helpers/speechHelper.js +108 -0
- package/lib/helpers/temperatureHelper.js +227 -0
- package/lib/helpers/timeHelper.js +88 -0
- package/lib/stateDefinitions/consumptionStates.js +82 -0
- package/lib/stateDefinitions/generalStates.js +68 -0
- package/lib/stateDefinitions/pumpStates.js +184 -0
- package/lib/stateDefinitions/runtimeStates.js +113 -0
- package/lib/stateDefinitions/solarStates.js +150 -0
- package/lib/stateDefinitions/speechStates.js +104 -0
- package/lib/stateDefinitions/temperatureStates.js +182 -0
- package/lib/stateDefinitions/timeStates.js +102 -0
- package/main.js +145 -0
- package/package.json +60 -0
|
Binary file
|
package/io-package.json
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
{
|
|
2
|
+
"common": {
|
|
3
|
+
"name": "poolcontrol",
|
|
4
|
+
"version": "0.0.7",
|
|
5
|
+
"news": {
|
|
6
|
+
"0.0.7": {
|
|
7
|
+
"en": "Added help file (help.md) and first README version",
|
|
8
|
+
"de": "Hilfedatei (help.md) und erste README-Version hinzugefügt",
|
|
9
|
+
"ru": "Добавлен файл справки (help.md) и первая версия README",
|
|
10
|
+
"pt": "Adicionado arquivo de ajuda (help.md) e primeira versão do README",
|
|
11
|
+
"nl": "Helpbestand (help.md) en eerste README-versie toegevoegd",
|
|
12
|
+
"fr": "Ajout du fichier d'aide (help.md) et première version du README",
|
|
13
|
+
"it": "Aggiunto file di aiuto (help.md) e prima versione di README",
|
|
14
|
+
"es": "Se agregó archivo de ayuda (help.md) y primera versión de README",
|
|
15
|
+
"pl": "Dodano plik pomocy (help.md) i pierwszą wersję README",
|
|
16
|
+
"uk": "Додано файл довідки (help.md) та першу версію README",
|
|
17
|
+
"zh-cn": "添加了帮助文件 (help.md) 和第一个 README 版本"
|
|
18
|
+
},
|
|
19
|
+
"0.0.6": {
|
|
20
|
+
"en": "Added consumption & cost calculation with external kWh meter",
|
|
21
|
+
"de": "Verbrauchs- und Kostenberechnung mit externem kWh-Zähler hinzugefügt",
|
|
22
|
+
"ru": "Добавлен расчет потребления и затрат с внешним счетчиком кВт·ч",
|
|
23
|
+
"pt": "Adicionado cálculo de consumo e custo com medidor externo de kWh",
|
|
24
|
+
"nl": "Verbruik- en kostenberekening toegevoegd met externe kWh-meter",
|
|
25
|
+
"fr": "Ajout du calcul de consommation et de coût avec compteur kWh externe",
|
|
26
|
+
"it": "Aggiunto calcolo di consumo e costi con contatore kWh esterno",
|
|
27
|
+
"es": "Se agregó cálculo de consumo y costos con medidor externo de kWh",
|
|
28
|
+
"pl": "Dodano obliczanie zużycia i kosztów z zewnętrznym licznikiem kWh",
|
|
29
|
+
"uk": "Додано розрахунок споживання та витрат із зовнішнім лічильником кВт·год",
|
|
30
|
+
"zh-cn": "新增使用外部千瓦时电表的消耗和成本计算"
|
|
31
|
+
},
|
|
32
|
+
"0.0.5": {
|
|
33
|
+
"en": "Added speech output via Alexa and Telegram",
|
|
34
|
+
"de": "Sprachausgaben über Alexa und Telegram hinzugefügt",
|
|
35
|
+
"ru": "Добавлен голосовой вывод через Alexa и Telegram",
|
|
36
|
+
"pt": "Adicionada saída de voz via Alexa e Telegram",
|
|
37
|
+
"nl": "Spraakuitvoer toegevoegd via Alexa en Telegram",
|
|
38
|
+
"fr": "Ajout de la sortie vocale via Alexa et Telegram",
|
|
39
|
+
"it": "Aggiunta uscita vocale tramite Alexa e Telegram",
|
|
40
|
+
"es": "Se agregó salida de voz a través de Alexa y Telegram",
|
|
41
|
+
"pl": "Dodano wyjście głosowe przez Alexa i Telegram",
|
|
42
|
+
"uk": "Додано голосовий вивід через Alexa та Telegram",
|
|
43
|
+
"zh-cn": "新增通过 Alexa 和 Telegram 的语音输出"
|
|
44
|
+
},
|
|
45
|
+
"0.0.4": {
|
|
46
|
+
"en": "Added runtime & circulation logic including new datapoint 'circulation.daily_required'",
|
|
47
|
+
"de": "Laufzeit- und Umwälzlogik hinzugefügt, einschließlich neuem Datenpunkt 'circulation.daily_required'",
|
|
48
|
+
"ru": "Добавлена логика времени работы и циркуляции, включая новую точку данных 'circulation.daily_required'",
|
|
49
|
+
"pt": "Adicionada lógica de tempo de execução e circulação, incluindo novo datapoint 'circulation.daily_required'",
|
|
50
|
+
"nl": "Runtime- en circulatielogica toegevoegd, inclusief nieuw datapoint 'circulation.daily_required'",
|
|
51
|
+
"fr": "Ajout de la logique de temps d'exécution et de circulation, y compris le nouveau point de données 'circulation.daily_required'",
|
|
52
|
+
"it": "Aggiunta logica di runtime e circolazione, incluso nuovo datapoint 'circulation.daily_required'",
|
|
53
|
+
"es": "Se agregó lógica de tiempo de ejecución y circulación, incluido nuevo datapoint 'circulation.daily_required'",
|
|
54
|
+
"pl": "Dodano logikę czasu pracy i cyrkulacji, w tym nowy punkt danych 'circulation.daily_required'",
|
|
55
|
+
"uk": "Додано логіку часу роботи та циркуляції, включаючи нову точку даних 'circulation.daily_required'",
|
|
56
|
+
"zh-cn": "新增运行时间和循环逻辑,包括新数据点 'circulation.daily_required'"
|
|
57
|
+
},
|
|
58
|
+
"0.0.3": {
|
|
59
|
+
"en": "Added time control with up to 3 configurable time windows",
|
|
60
|
+
"de": "Zeitsteuerung mit bis zu 3 konfigurierbaren Zeitfenstern hinzugefügt",
|
|
61
|
+
"ru": "Добавлено управление временем с до 3 настраиваемыми временными окнами",
|
|
62
|
+
"pt": "Adicionado controle de tempo com até 3 janelas de tempo configuráveis",
|
|
63
|
+
"nl": "Tijdregeling toegevoegd met maximaal 3 configureerbare tijdvensters",
|
|
64
|
+
"fr": "Ajout du contrôle du temps avec jusqu'à 3 fenêtres de temps configurables",
|
|
65
|
+
"it": "Aggiunto controllo del tempo con fino a 3 finestre temporali configurabili",
|
|
66
|
+
"es": "Se agregó control de tiempo con hasta 3 ventanas de tiempo configurables",
|
|
67
|
+
"pl": "Dodano kontrolę czasu z maksymalnie 3 konfigurowalnymi oknami czasowymi",
|
|
68
|
+
"uk": "Додано керування часом з до 3 налаштовуваними часовими вікнами",
|
|
69
|
+
"zh-cn": "新增时间控制,最多可配置 3 个时间窗口"
|
|
70
|
+
},
|
|
71
|
+
"0.0.2": {
|
|
72
|
+
"en": "Extended pump logic with error detection and safety functions",
|
|
73
|
+
"de": "Pumpenlogik erweitert mit Fehlererkennung und Sicherheitsfunktionen",
|
|
74
|
+
"ru": "Расширена логика насоса с обнаружением ошибок и функциями безопасности",
|
|
75
|
+
"pt": "Lógica da bomba estendida com detecção de erros e funções de segurança",
|
|
76
|
+
"nl": "Pomplogica uitgebreid met foutdetectie en veiligheidsfuncties",
|
|
77
|
+
"fr": "Logique de pompe étendue avec détection d'erreurs et fonctions de sécurité",
|
|
78
|
+
"it": "Logica della pompa estesa con rilevamento errori e funzioni di sicurezza",
|
|
79
|
+
"es": "Lógica de la bomba ampliada con detección de errores y funciones de seguridad",
|
|
80
|
+
"pl": "Rozszerzona logika pompy z wykrywaniem błędów i funkcjami bezpieczeństwa",
|
|
81
|
+
"uk": "Розширена логіка насоса з виявленням помилок та функціями безпеки",
|
|
82
|
+
"zh-cn": "扩展泵逻辑,具有错误检测和安全功能"
|
|
83
|
+
},
|
|
84
|
+
"0.0.1": {
|
|
85
|
+
"en": "initial release",
|
|
86
|
+
"de": "Erstveröffentlichung",
|
|
87
|
+
"ru": "Начальная версия",
|
|
88
|
+
"pt": "lançamento inicial",
|
|
89
|
+
"nl": "Eerste uitgave",
|
|
90
|
+
"fr": "Première version",
|
|
91
|
+
"it": "Versione iniziale",
|
|
92
|
+
"es": "Versión inicial",
|
|
93
|
+
"pl": "Pierwsze wydanie",
|
|
94
|
+
"uk": "Початкова версія",
|
|
95
|
+
"zh-cn": "首次出版"
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"titleLang": {
|
|
99
|
+
"en": "PoolControl",
|
|
100
|
+
"de": "PoolControl",
|
|
101
|
+
"ru": "PoolControl",
|
|
102
|
+
"pt": "PoolControl",
|
|
103
|
+
"nl": "PoolControl",
|
|
104
|
+
"fr": "PoolControl",
|
|
105
|
+
"it": "PoolControl",
|
|
106
|
+
"es": "PoolControl",
|
|
107
|
+
"pl": "PoolControl",
|
|
108
|
+
"uk": "PoolControl",
|
|
109
|
+
"zh-cn": "PoolControl"
|
|
110
|
+
},
|
|
111
|
+
"desc": {
|
|
112
|
+
"en": "Adapter for controlling and monitoring pool systems (pump, temperature, solar, consumption, speech output).",
|
|
113
|
+
"de": "Adapter zur Steuerung und Überwachung von Poolanlagen (Pumpe, Temperatur, Solar, Verbrauch, Sprachausgaben).",
|
|
114
|
+
"ru": "Адаптер для управления и мониторинга бассейнов (насос, температура, солнечная система, потребление, голосовые оповещения).",
|
|
115
|
+
"pt": "Adaptador para controlar e monitorar sistemas de piscina (bomba, temperatura, solar, consumo, saída de voz).",
|
|
116
|
+
"nl": "Adapter voor het besturen en bewaken van zwembadsystemen (pomp, temperatuur, zonne-energie, verbruik, spraakuitvoer).",
|
|
117
|
+
"fr": "Adaptateur pour contrôler et surveiller les systèmes de piscine (pompe, température, solaire, consommation, sorties vocales).",
|
|
118
|
+
"it": "Adattatore per il controllo e il monitoraggio dei sistemi della piscina (pompa, temperatura, solare, consumo, uscite vocali).",
|
|
119
|
+
"es": "Adaptador para controlar y supervisar sistemas de piscina (bomba, temperatura, solar, consumo, salida de voz).",
|
|
120
|
+
"pl": "Adapter do sterowania i monitorowania systemów basenowych (pompa, temperatura, solar, zużycie, komunikaty głosowe).",
|
|
121
|
+
"uk": "Адаптер для керування та моніторингу басейнових систем (насос, температура, сонячна енергія, споживання, голосові повідомлення).",
|
|
122
|
+
"zh-cn": "用于控制和监控泳池系统的适配器(泵、温度、太阳能、能耗、语音输出)。"
|
|
123
|
+
},
|
|
124
|
+
"authors": [
|
|
125
|
+
"DasBo1975 <dasbo1975@outlook.de>"
|
|
126
|
+
],
|
|
127
|
+
"keywords": [
|
|
128
|
+
"pool",
|
|
129
|
+
"pumpe",
|
|
130
|
+
"solar",
|
|
131
|
+
"wasser",
|
|
132
|
+
"temperatur"
|
|
133
|
+
],
|
|
134
|
+
"licenseInformation": {
|
|
135
|
+
"type": "free",
|
|
136
|
+
"license": "MIT"
|
|
137
|
+
},
|
|
138
|
+
"platform": "Javascript/Node.js",
|
|
139
|
+
"icon": "poolcontrol.png",
|
|
140
|
+
"enabled": true,
|
|
141
|
+
"extIcon": "https://raw.githubusercontent.com/DasBo1975/ioBroker.poolcontrol/main/admin/poolcontrol.png",
|
|
142
|
+
"readme": "https://github.com/DasBo1975/ioBroker.poolcontrol/blob/main/README.md",
|
|
143
|
+
"loglevel": "info",
|
|
144
|
+
"tier": 3,
|
|
145
|
+
"mode": "daemon",
|
|
146
|
+
"type": "climate-control",
|
|
147
|
+
"compact": true,
|
|
148
|
+
"connectionType": "local",
|
|
149
|
+
"dataSource": "poll",
|
|
150
|
+
"adminUI": {
|
|
151
|
+
"config": "json"
|
|
152
|
+
},
|
|
153
|
+
"dependencies": [
|
|
154
|
+
{
|
|
155
|
+
"js-controller": ">=6.0.11"
|
|
156
|
+
}
|
|
157
|
+
],
|
|
158
|
+
"globalDependencies": [
|
|
159
|
+
{
|
|
160
|
+
"admin": ">=7.0.23"
|
|
161
|
+
}
|
|
162
|
+
]
|
|
163
|
+
},
|
|
164
|
+
"native": {
|
|
165
|
+
"option1": true,
|
|
166
|
+
"option2": "42"
|
|
167
|
+
},
|
|
168
|
+
"objects": [],
|
|
169
|
+
"instanceObjects": [],
|
|
170
|
+
"support": {
|
|
171
|
+
"donate": {
|
|
172
|
+
"paypal": "https://www.paypal.com/donate?business=dirk.bertin%40t-online.de"
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
// This file extends the AdapterConfig type from "@types/iobroker"
|
|
2
|
+
// using the actual properties present in io-package.json
|
|
3
|
+
// in order to provide typings for adapter.config properties
|
|
4
|
+
|
|
5
|
+
import { native } from "../io-package.json";
|
|
6
|
+
|
|
7
|
+
type _AdapterConfig = typeof native;
|
|
8
|
+
|
|
9
|
+
// Augment the globally declared type ioBroker.AdapterConfig
|
|
10
|
+
declare global {
|
|
11
|
+
namespace ioBroker {
|
|
12
|
+
interface AdapterConfig extends _AdapterConfig {
|
|
13
|
+
// Do not enter anything here!
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// this is required so the above AdapterConfig is found by TypeScript / type checking
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* consumptionHelper
|
|
5
|
+
* - Nutzt externen kWh-Zähler (objectId aus Config)
|
|
6
|
+
* - Berechnet Periodenwerte (Tag/Woche/Monat/Jahr)
|
|
7
|
+
* - Berechnet Kosten anhand Strompreis (€/kWh)
|
|
8
|
+
* - Offset-Mechanismus: summiert alte Werte bei Zählerwechsel/Reset auf
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const consumptionHelper = {
|
|
12
|
+
adapter: null,
|
|
13
|
+
energyId: null, // Objekt-ID des externen kWh-Zählers
|
|
14
|
+
price: 0, // Strompreis €/kWh
|
|
15
|
+
baselines: {}, // { day, week, month, year }
|
|
16
|
+
resetTimer: null,
|
|
17
|
+
|
|
18
|
+
init(adapter) {
|
|
19
|
+
this.adapter = adapter;
|
|
20
|
+
this.energyId = adapter.config.external_energy_total_id || null;
|
|
21
|
+
this.price = adapter.config.energy_price_eur_kwh || 0;
|
|
22
|
+
|
|
23
|
+
if (this.energyId) {
|
|
24
|
+
adapter.subscribeForeignStates(this.energyId);
|
|
25
|
+
adapter.log.info(`[consumptionHelper] Überwache externen kWh-Zähler: ${this.energyId}`);
|
|
26
|
+
} else {
|
|
27
|
+
adapter.log.info('[consumptionHelper] Kein externer kWh-Zähler konfiguriert → Verbrauchslogik inaktiv.');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Reset-Timer für Mitternacht
|
|
31
|
+
this._scheduleDailyReset();
|
|
32
|
+
},
|
|
33
|
+
|
|
34
|
+
async handleStateChange(id, state) {
|
|
35
|
+
if (!state) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
if (id !== this.energyId) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const totalNowRaw = Number(state.val);
|
|
43
|
+
if (!Number.isFinite(totalNowRaw)) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
await this._updateConsumption(totalNowRaw);
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
async _updateConsumption(totalNowRaw) {
|
|
51
|
+
try {
|
|
52
|
+
// Offset laden
|
|
53
|
+
const offset = (await this.adapter.getStateAsync('consumption.offset_kwh'))?.val || 0;
|
|
54
|
+
const last = (await this.adapter.getStateAsync('consumption.last_total_kwh'))?.val || 0;
|
|
55
|
+
|
|
56
|
+
let totalNow = totalNowRaw;
|
|
57
|
+
|
|
58
|
+
// Prüfen: Reset oder neues Gerät?
|
|
59
|
+
if (totalNowRaw < last) {
|
|
60
|
+
this.adapter.log.warn('[consumptionHelper] Zähler-Reset erkannt → Offset wird angepasst');
|
|
61
|
+
const newOffset = offset + last;
|
|
62
|
+
await this.adapter.setStateAsync('consumption.offset_kwh', {
|
|
63
|
+
val: newOffset,
|
|
64
|
+
ack: true,
|
|
65
|
+
});
|
|
66
|
+
totalNow = newOffset + totalNowRaw;
|
|
67
|
+
} else {
|
|
68
|
+
totalNow = offset + totalNowRaw;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Aktuellen Gesamtwert setzen
|
|
72
|
+
await this.adapter.setStateAsync('consumption.total_kwh', {
|
|
73
|
+
val: totalNow,
|
|
74
|
+
ack: true,
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
// Baselines laden (falls leer)
|
|
78
|
+
if (Object.keys(this.baselines).length === 0) {
|
|
79
|
+
await this._loadBaselines(totalNow);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Differenzen berechnen
|
|
83
|
+
const values = {
|
|
84
|
+
day: totalNow - (this.baselines.day ?? totalNow),
|
|
85
|
+
week: totalNow - (this.baselines.week ?? totalNow),
|
|
86
|
+
month: totalNow - (this.baselines.month ?? totalNow),
|
|
87
|
+
year: totalNow - (this.baselines.year ?? totalNow),
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// Negative Werte abfangen (bei Reset)
|
|
91
|
+
for (const key of Object.keys(values)) {
|
|
92
|
+
if (values[key] < 0) {
|
|
93
|
+
this.baselines[key] = totalNow;
|
|
94
|
+
values[key] = 0;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// In States schreiben
|
|
99
|
+
await this.adapter.setStateAsync('consumption.day_kwh', {
|
|
100
|
+
val: Number(values.day.toFixed(3)),
|
|
101
|
+
ack: true,
|
|
102
|
+
});
|
|
103
|
+
await this.adapter.setStateAsync('consumption.week_kwh', {
|
|
104
|
+
val: Number(values.week.toFixed(3)),
|
|
105
|
+
ack: true,
|
|
106
|
+
});
|
|
107
|
+
await this.adapter.setStateAsync('consumption.month_kwh', {
|
|
108
|
+
val: Number(values.month.toFixed(3)),
|
|
109
|
+
ack: true,
|
|
110
|
+
});
|
|
111
|
+
await this.adapter.setStateAsync('consumption.year_kwh', {
|
|
112
|
+
val: Number(values.year.toFixed(3)),
|
|
113
|
+
ack: true,
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Kosten berechnen
|
|
117
|
+
if (this.price > 0) {
|
|
118
|
+
await this.adapter.setStateAsync('costs.day_eur', {
|
|
119
|
+
val: Number((values.day * this.price).toFixed(2)),
|
|
120
|
+
ack: true,
|
|
121
|
+
});
|
|
122
|
+
await this.adapter.setStateAsync('costs.week_eur', {
|
|
123
|
+
val: Number((values.week * this.price).toFixed(2)),
|
|
124
|
+
ack: true,
|
|
125
|
+
});
|
|
126
|
+
await this.adapter.setStateAsync('costs.month_eur', {
|
|
127
|
+
val: Number((values.month * this.price).toFixed(2)),
|
|
128
|
+
ack: true,
|
|
129
|
+
});
|
|
130
|
+
await this.adapter.setStateAsync('costs.year_eur', {
|
|
131
|
+
val: Number((values.year * this.price).toFixed(2)),
|
|
132
|
+
ack: true,
|
|
133
|
+
});
|
|
134
|
+
await this.adapter.setStateAsync('costs.total_eur', {
|
|
135
|
+
val: Number((totalNow * this.price).toFixed(2)),
|
|
136
|
+
ack: true,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// Letzten Stand des externen Zählers merken
|
|
141
|
+
await this.adapter.setStateAsync('consumption.last_total_kwh', {
|
|
142
|
+
val: totalNowRaw,
|
|
143
|
+
ack: true,
|
|
144
|
+
});
|
|
145
|
+
} catch (err) {
|
|
146
|
+
this.adapter.log.warn(`[consumptionHelper] Fehler bei Verbrauchsupdate: ${err.message}`);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
async _loadBaselines(totalNow) {
|
|
151
|
+
this.baselines = {};
|
|
152
|
+
const day = (await this.adapter.getStateAsync('consumption.day_kwh'))?.val;
|
|
153
|
+
const week = (await this.adapter.getStateAsync('consumption.week_kwh'))?.val;
|
|
154
|
+
const month = (await this.adapter.getStateAsync('consumption.month_kwh'))?.val;
|
|
155
|
+
const year = (await this.adapter.getStateAsync('consumption.year_kwh'))?.val;
|
|
156
|
+
|
|
157
|
+
this.baselines.day = totalNow - (day || 0);
|
|
158
|
+
this.baselines.week = totalNow - (week || 0);
|
|
159
|
+
this.baselines.month = totalNow - (month || 0);
|
|
160
|
+
this.baselines.year = totalNow - (year || 0);
|
|
161
|
+
|
|
162
|
+
this.adapter.log.info(`[consumptionHelper] Baselines geladen: ${JSON.stringify(this.baselines)}`);
|
|
163
|
+
},
|
|
164
|
+
|
|
165
|
+
_scheduleDailyReset() {
|
|
166
|
+
const now = new Date();
|
|
167
|
+
const nextMidnight = new Date(now);
|
|
168
|
+
nextMidnight.setHours(24, 0, 0, 0);
|
|
169
|
+
const msUntilMidnight = nextMidnight - now;
|
|
170
|
+
|
|
171
|
+
this.resetTimer = setTimeout(() => {
|
|
172
|
+
this.baselines = {};
|
|
173
|
+
this._scheduleDailyReset();
|
|
174
|
+
}, msUntilMidnight);
|
|
175
|
+
},
|
|
176
|
+
|
|
177
|
+
cleanup() {
|
|
178
|
+
if (this.resetTimer) {
|
|
179
|
+
clearTimeout(this.resetTimer);
|
|
180
|
+
this.resetTimer = null;
|
|
181
|
+
}
|
|
182
|
+
},
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
module.exports = consumptionHelper;
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* frostHelper
|
|
5
|
+
* - Prüft Außentemperatur gegen Frostschutz-Grenze
|
|
6
|
+
* - Schaltet Pumpe bei Frost ein (nur im Modus "auto")
|
|
7
|
+
* - Kleine Hysterese: +1°C zum Ausschalten
|
|
8
|
+
* - Schaltet über den zentralen Bool-State pump.pump_switch
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const frostHelper = {
|
|
12
|
+
adapter: null,
|
|
13
|
+
checkTimer: null,
|
|
14
|
+
|
|
15
|
+
init(adapter) {
|
|
16
|
+
this.adapter = adapter;
|
|
17
|
+
|
|
18
|
+
// Minütlicher Check
|
|
19
|
+
this._scheduleCheck();
|
|
20
|
+
|
|
21
|
+
this.adapter.log.info('[frostHelper] initialisiert (Prüfung alle 60s)');
|
|
22
|
+
},
|
|
23
|
+
|
|
24
|
+
_scheduleCheck() {
|
|
25
|
+
if (this.checkTimer) {
|
|
26
|
+
clearInterval(this.checkTimer);
|
|
27
|
+
}
|
|
28
|
+
this.checkTimer = setInterval(() => this._checkFrost(), 60 * 1000);
|
|
29
|
+
// Beim Start sofort prüfen
|
|
30
|
+
this._checkFrost();
|
|
31
|
+
},
|
|
32
|
+
|
|
33
|
+
async _checkFrost() {
|
|
34
|
+
try {
|
|
35
|
+
// Nur aktiv im AUTO-Modus
|
|
36
|
+
const mode = (await this.adapter.getStateAsync('pump.mode'))?.val;
|
|
37
|
+
if (mode !== 'auto') {
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Frostschutz aktiviert?
|
|
42
|
+
const frostActive = (await this.adapter.getStateAsync('pump.frost_protection_active'))?.val;
|
|
43
|
+
if (!frostActive) {
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Grenztemperatur laden
|
|
48
|
+
const frostTemp = (await this.adapter.getStateAsync('pump.frost_protection_temp'))?.val;
|
|
49
|
+
if (frostTemp == null) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Außentemperatur laden
|
|
54
|
+
const outside = (await this.adapter.getStateAsync('temperature.outside.current'))?.val;
|
|
55
|
+
if (outside == null) {
|
|
56
|
+
this.adapter.log.debug('[frostHelper] Keine Außentemperatur verfügbar');
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Aktueller Pumpenzustand (zentraler Bool)
|
|
61
|
+
const pumpActive = (await this.adapter.getStateAsync('pump.pump_switch'))?.val;
|
|
62
|
+
let shouldRun = pumpActive;
|
|
63
|
+
|
|
64
|
+
// Logik: einschalten bei <= frostTemp, ausschalten bei >= frostTemp+1
|
|
65
|
+
if (outside <= frostTemp) {
|
|
66
|
+
shouldRun = true;
|
|
67
|
+
} else if (outside >= frostTemp + 1) {
|
|
68
|
+
shouldRun = false;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Schalten nur, wenn sich etwas ändert
|
|
72
|
+
if (shouldRun !== pumpActive) {
|
|
73
|
+
await this.adapter.setStateAsync('pump.pump_switch', {
|
|
74
|
+
val: shouldRun,
|
|
75
|
+
ack: false,
|
|
76
|
+
});
|
|
77
|
+
this.adapter.log.info(
|
|
78
|
+
`[frostHelper] Frostschutz → Pumpe ${shouldRun ? 'EIN' : 'AUS'} (Außen=${outside}°C, Grenze=${frostTemp}°C)`,
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
} catch (err) {
|
|
82
|
+
this.adapter.log.warn(`[frostHelper] Fehler im Check: ${err.message}`);
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
cleanup() {
|
|
87
|
+
if (this.checkTimer) {
|
|
88
|
+
clearInterval(this.checkTimer);
|
|
89
|
+
this.checkTimer = null;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
module.exports = frostHelper;
|