iobroker.smartloadmanager 0.1.2 → 0.4.1

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,275 @@
1
+ ![Logo](../../admin/smartloadmanager.png)
2
+
3
+ # ioBroker.smartloadmanager
4
+
5
+ ## Version 0.2.0 examples
6
+
7
+ Requires Node.js >=22.19.0. Device commands use `ack: false`; the device adapter
8
+ provides the acknowledgement. Feed-in must represent grid connection power in watts.
9
+ Enable `feedinNegativ` when export is negative. Baseload is an additional reserve;
10
+ do not subtract household consumption twice if it is already included in the meter reading.
11
+
12
+ ### Binary loads and heating
13
+
14
+ Example: `performance: 1000`, `switchOnPoint: 1200`, `switchOffPoint: 0`,
15
+ `switchOnDelay: 20`, `switchOffDelay: 10`. A 1200 W surplus after baseload starts
16
+ the on timer. Import or surplus below the off threshold starts the off timer.
17
+ `minimumOnMinutes: 10` and `minimumOffMinutes: 5` reduce cycling; import above
18
+ `forecastMaxGridImport` overrides minimum runtime. Set minimum times to 0 to disable them.
19
+
20
+ Use `datapoint` for boolean switching and `numericDatapoint` for numeric 0/1.
21
+ Heating consumers additionally support temperature, enable and heartbeat inputs.
22
+ Maximum temperature and missing enable/heartbeat prevent activation.
23
+ Heartbeat checks the input's truth value, not the age of its last update.
24
+ A missing or nonfinite configured temperature sensor prevents activation and
25
+ stops automatic heating. “Switch off only at switch-off time” intentionally
26
+ disables normal surplus shutdown until the time window ends; heating protection remains active.
27
+
28
+ ### Percentage and battery control
29
+
30
+ A 2000 W percent consumer at 50% consumes 1000 W. With 400 W additional export,
31
+ the target becomes 70%. With 200 W import it becomes 40%. Below `minPercentStart`,
32
+ the target becomes 0%. Disabled consumers are not regulated. This calculation
33
+ assumes the load is included in the grid meter reading.
34
+
35
+ Configure a battery's `batterySetpoint`, `performance`, and optional `batterySOC`
36
+ and `batteryTargetSOC`. With 800 W surplus and a 1500 W rating, the charging command
37
+ is 800 W. Reaching target SOC sets charging power to 0. The optional global battery
38
+ mode datapoint uses 0 (off), 1 (target reached), and 2 (charging); verify that these
39
+ values match the device adapter.
40
+
41
+ `controlMode`: 0 = off, 1 = manual on, 2 = automatic. Manual on sets a percent load
42
+ to 100% or battery charging power to its configured rating. Automatic regulators
43
+ respect manual modes.
44
+ Battery control includes already requested charging power and corrects it by the
45
+ measured grid surplus. `batteryDelaySeconds` and `delaySecondsProzent` remain
46
+ effective during frequent meter updates; 0 means immediate regulation.
47
+ Missing configured SOC/target SOC values stop charging. Time windows are also
48
+ checked every 15 seconds. Disabling a consumer cancels pending switches but leaves
49
+ its existing device state unchanged; use `controlMode: 0` to switch it off.
50
+
51
+ ### PV forecast example
52
+
53
+ Enable `enableForecast`, select e.g. `pvforecast.0.summary.JSONData` as
54
+ `forecastJsonDatapoint`, and enable `useForecast` for the consumer.
55
+ Alternatively select `pvforecast.0.plants.<plant name>.JSONData` to use one plant.
56
+ Both global forecast control and the individual consumer's forecast option must be
57
+ enabled. Decision notifications do not enable forecast control and are emitted only
58
+ when a forecast actually affects control. Version 0.2.0 logs imports at debug level.
59
+ Version 0.4.1 logs configuration, imports and enabled decision
60
+ notifications only at debug level, suppressing identical decisions for five minutes.
61
+ External delivery additionally requires Notification Manager or a messenger service.
62
+ The expected JSON array uses Unix timestamps in milliseconds:
63
+
64
+ ```js
65
+ const now = Date.now();
66
+ const forecast = [0, 15, 30, 45].map(minutes => ({
67
+ t: now + minutes * 60000,
68
+ y: 3.5,
69
+ }));
70
+ // JSON.stringify(forecast), with forecastPowerUnit = "kW"
71
+ ```
72
+
73
+ With 500 W baseload this gives 3000 W forecast surplus, reduced to 2400 W at
74
+ `forecastSafetyPercent: 80`. A 2000 W consumer requires a sustained
75
+ `forecastMinMinutes: 30` window starting within `forecastMaxStartMinutes: 30`.
76
+ At `forecastMinCurrentPercent: 50`, at least 1000 W must already be available.
77
+
78
+ `forecastBridgeMinutes: 5` limits dip bridging; `forecastMaxGridImport: 100`
79
+ prevents bridging above 100 W import. Past forecast points are ignored.
80
+ Select `W` explicitly for small watt readings: automatic detection treats values
81
+ up to 100 as kW. Inspect `forecast.*` and each consumer's forecast status states.
82
+
83
+ Time windows may cross midnight (22:00–06:00). Equal start/end times mean all day.
84
+ Automated tests use simulated devices; no physical device installation was available.
85
+
86
+ ## 🔧 Description
87
+
88
+ The **smartloadmanager** adapter dynamically controls consumers based on a PV feed-in value.
89
+ The goal is to consume surplus power locally and minimize or completely avoid feeding into the public grid.
90
+ It supports binary consumers, percentage-controllable devices, and battery storage systems.
91
+
92
+ ---
93
+
94
+ ## 🚀 Features
95
+
96
+ - ✅ Monitoring of a configurable feed-in datapoint
97
+ - ✅ Dynamic switching on of consumers in case of surplus
98
+ - ✅ Dynamic switching off in case of deficit or grid consumption
99
+ - ✅ Supports **binary**, **percent**, **heating** and **battery** consumers
100
+ - ✅ Percentage control with delay (smooth down-regulation), optionally configurable per consumer
101
+ - ✅ Dynamic charging power for battery storage with target SOC
102
+ - ✅ Time windows for on/off logic per consumer (incl. "only at off-time")
103
+ - ✅ Automatic object creation with extended information per consumer
104
+ - ✅ Control mode: Off / Manual On / Auto per consumer
105
+ - ✅ Hysteresis control via separate on/off thresholds
106
+ - ✅ Consumer-specific switching delay (override of global setting)
107
+ - ✅ Global battery switching delay (`batteryDelaySeconds`)
108
+ - ✅ Write check for battery control mode (debug output in log)
109
+ - ✅ **Delayed switching via timers** for on and off
110
+ - ✅ **Display of active timers in debug log** with remaining time
111
+ - ✅ Optional **notifications** for binary, percent, battery and heating (Telegram / Gotify)
112
+ - ✅ Optimization for 0/1 datapoints (boolean or numeric 0/1 control)
113
+ - ✅ Detailed logging of all timers, surpluses and switching operations
114
+ - ✅ Notifications per consumer type (Binary, Percent, Battery, Heating), optionally enabled
115
+ - ✅ Heating/immersion heater control with temperature, enable and heartbeat checks
116
+
117
+ ---
118
+
119
+ ## ⚙️ Configuration
120
+
121
+ ### 🔹 Main settings
122
+
123
+ | Setting | Description |
124
+ | ---------------------------------- | ------------------------------------------------------------------------------- |
125
+ | **Feed-in datapoint** | Object ID of the feed-in value (e.g. PV surplus) |
126
+ | **Base load** | Always subtracted from feed-in value (e.g. standby consumption) |
127
+ | **Switch-on threshold** | Threshold in watts at which consumers are switched on |
128
+ | **Switch-off threshold** | Threshold in watts at which consumers are switched off |
129
+ | **Delay (seconds)** | Delay for binary switching (hysteresis smoothing) |
130
+ | **Delay percent (s)** | Global delay for down-regulation of percent consumers (e.g. wallbox) |
131
+ | **Delay battery (s)** | Global delay for battery control |
132
+ | **Feed-in negative** | If active: negative = feed-in / positive = grid consumption |
133
+ | **Battery control mode datapoint** | Optional datapoint for battery mode switching (Auto/Manual/Off) |
134
+ | **Notifications** | Enable notifications for Binary / Percent / Battery / Heating (Telegram/Gotify) |
135
+
136
+ ---
137
+
138
+ ### 🔹 Consumers
139
+
140
+ | Field | Description |
141
+ | ----------------------------------- | --------------------------------------------------------------------- |
142
+ | **Name** | Display name |
143
+ | **Active** | Enables control for this consumer |
144
+ | **Rule type** | `"binary"`, `"percent"`, `"battery"`, `"heating"` |
145
+ | **Control datapoint** | ID to switch or regulate, supports 0/1 datapoints |
146
+ | **Power [W]** | Realistic electrical power of the consumer |
147
+ | **Switch-on threshold [W]** | Required surplus for activation |
148
+ | **Switch-off threshold [W]** | Lower limit for deactivation |
149
+ | **Max power [W]** | Reference value for percentage control |
150
+ | **Delay percent [s]** | Optional delay for down-regulation for this consumer only |
151
+ | **Delay override [s]** | Consumer-specific switching delay (e.g. immediate switching possible) |
152
+ | **Switch-on time (HH:MM)** | Time when control may start |
153
+ | **Switch-off time (HH:MM)** | Time when control must end |
154
+ | **Only off at time** | Checkbox: switch off only at configured time |
155
+ | **batterySetpoint (battery only)** | Datapoint where desired charging power is written |
156
+ | **batterySOC / targetSOC** | Optional: SOC & target SOC to stop charging at target |
157
+ | **Timer / TimerEnd** | Internal timer for delayed switching, remaining time visible in log |
158
+ | **minTemperature / maxTemperature** | Lower/upper threshold for heating operation |
159
+ | **temperatureDatapoint (heating)** | Temperature datapoint for min/max checks |
160
+ | **enableDatapoint (heating)** | Optional datapoint: heating only allowed if true |
161
+ | **heartbeatDatapoint (heating)** | Optional: must cyclically deliver true, otherwise switched off |
162
+
163
+ ---
164
+
165
+ ## 🔋 Battery storage support
166
+
167
+ - Consumers with `"ruletype": "battery"` control the charging setpoint based on current surplus.
168
+ - If `batterySOC` and `batteryTargetSOC` are set, charging stops at target SOC.
169
+ - Optional `batteryControlModeDatapoint`:
170
+ - `0 = Off`, `1 = Manual`, `2 = Auto`
171
+ - Control is only active within configured time windows.
172
+ - Control runs on every feed-in update, after configured delay (`batteryDelaySeconds`).
173
+ - Delays for all consumer types (binary, percent, battery) can be set globally or per consumer.
174
+ - Detailed logging and optional notifications for battery switching.
175
+
176
+ ---
177
+
178
+ ## 🌡️ Heating support
179
+
180
+ - Consumers with `"ruletype": "heating"` are intended for immersion heaters or similar devices.
181
+ - Control only occurs when:
182
+ - Current temperature is below `maxTemperature`
183
+ - Optional: above `minTemperature`
184
+ - Optional: `enableDatapoint` is active
185
+ - Optional: `heartbeatDatapoint` is valid
186
+ - If conditions are not met, switching is stopped immediately and timers are canceled.
187
+ - Supports on/off timers (`onTimerRemaining` / `offTimerRemaining`) with remaining time display.
188
+ - Detailed logging and optional notifications for heating consumers.
189
+
190
+ ---
191
+
192
+ ## 🧠 Control logic
193
+
194
+ 1. **Feed-in datapoint value is interpreted** (positive = grid consumption or feed-in, depending on config)
195
+ 2. **Surplus > base load + switch-on threshold**:
196
+ → Consumers are switched on (binary) in ascending order of power
197
+ 3. **Deficit < base load - switch-off threshold**:
198
+ → Consumers are switched off in reverse order
199
+ 4. **Percent consumers**:
200
+ → % = surplus / max power
201
+ → Adjusted after delay (global or per consumer)
202
+ → Optional overrides possible
203
+ 5. **Battery consumers**:
204
+ → Charging power = min(surplus, max power), if target SOC not reached
205
+ → Delay via `batteryDelaySeconds` or per consumer
206
+ 6. **Time window check for all consumers**
207
+ 7. **Control only in mode Auto (2)**
208
+ 8. **Delayed switching via timers** (cancel/restart on updates, remaining time visible in log)
209
+ 9. **Optional notifications for all switching/regulation types**
210
+ 10. **0/1 datapoint optimization** (boolean or numeric 0/1 supported)
211
+ 11. **Detailed logging of all switching and surplus values**
212
+ 12. **Heating logic**: switch only within temp ranges and with valid enable/heartbeat datapoints
213
+
214
+ ---
215
+
216
+ ## 💡 Example: Wallbox
217
+
218
+ | Parameter | Value |
219
+ | --------- | ------- |
220
+ | Feed-in | 1000 W |
221
+ | Base load | 100 W |
222
+ | Max power | 11000 W |
223
+
224
+ **Calculation:**
225
+
226
+ - Surplus: 1000 – 100 = 900 W
227
+ - Percent: 900 / 11000 ≈ 8.2 % → Wallbox set to 8 %
228
+
229
+ ---
230
+
231
+ ## 📋 Object structure
232
+
233
+ Each consumer gets its own channel with the following states:
234
+
235
+ - `.controlMode` → 0 = Off, 1 = Manual, 2 = Auto
236
+ - `.switchOnTime` / `.switchOffTime`
237
+ - `.alwaysOffAtTime` → true/false
238
+ - `.performance`, `.switchOnPoint`, `.switchOffPoint`
239
+ - `.batterySetpoint` (battery only)
240
+ - `.temperatureDatapoint` (heating only)
241
+ - `.onTimerRemaining / .offTimerRemaining` → Remaining time for timers
242
+ - `.timer` → internal timer for delayed switching
243
+ - `.timerEnd` → timestamp when timer expires (for log)
244
+
245
+ ---
246
+
247
+ ## 🚫 Limitations
248
+
249
+ - No SOC history, no long-term logic
250
+ - No multiple use of identical datapoints
251
+ - Battery control based only on feed-in, not discharge power
252
+ - Heating without direct power measurement – only thresholds and enable checks
253
+
254
+ ---
255
+
256
+ ## 🛣️ Future features
257
+
258
+ - ~~PV forecast-based control (beta)~~
259
+ - Combined consumer support
260
+ - ~~Prioritization profiles~~
261
+ - Overheating or fault handling per device
262
+ - ~~Min/max percentage limits~~
263
+ - Configurable group or room logic
264
+
265
+ ---
266
+
267
+ ## PV forecast control
268
+
269
+ SmartLoadManager can use `pvforecast.<instance>.summary.JSONData`. Enable forecast control, select the JSON data point and enable it for the desired consumers.
270
+
271
+ The adapter evaluates a continuous forecast window instead of a single value. A configurable safety factor reduces optimistic forecasts. Forecast-assisted switch-on additionally requires part of the required power to be present as real surplus. Short drops can be bridged for a limited time, but only up to the configured maximum grid import.
272
+
273
+ Minimum run and off times prevent rapid cycling. Forecast decisions never override time windows, heating temperature limits, hardware enable/heartbeat signals or battery SOC limits.
274
+
275
+ Recommended starting values: 80% safety factor, 30-minute forecast window, 30-minute maximum lead time, 50% current-surplus requirement, 5-minute dip bridging, 100 W maximum grid import and 10/5-minute minimum run/off times.
package/io-package.json CHANGED
@@ -1,22 +1,35 @@
1
1
  {
2
2
  "common": {
3
3
  "name": "smartloadmanager",
4
- "version": "0.1.2",
4
+ "version": "0.4.1",
5
5
  "news": {
6
- "0.1.2": {
7
- "en": "Update dependencies",
8
- "de": "Abhängigkeiten aktualisieren",
9
- "ru": "Обновление зависимостей",
10
- "pt": "Atualizar dependências",
11
- "nl": "Afhankelijkheden bijwerken",
12
- "fr": "Mise à jour des dépendances",
13
- "it": "Dipendenze di aggiornamento",
14
- "es": "Actualizar las dependencias",
15
- "pl": "Aktualizuj zależności",
16
- "uk": "Оновлення залежності",
17
- "zh-cn": "更新依赖关系"
6
+ "0.4.1": {
7
+ "en": "Fix forecast notifications for percentage and battery consumers, limit duplicate notifications, clear invalid forecast data and log all forecast diagnostics only at debug level.",
8
+ "de": "Prognosemeldungen für Prozent- und Batterieverbraucher korrigiert, doppelte Meldungen begrenzt, ungültige Prognosedaten verworfen und sämtliche Prognosediagnosen ausschließlich auf Debug-Stufe protokolliert.",
9
+ "ru": "Исправлены уведомления прогноза для процентных потребителей и батарей, ограничены повторы, очищены недействительные данные; диагностика прогноза выводится только на уровне debug.",
10
+ "pt": "Corrigidas notificações de previsão para consumidores percentuais e baterias, limitadas repetições, eliminados dados inválidos e diagnóstico apenas no nível debug.",
11
+ "nl": "Prognosemeldingen voor procentuele verbruikers en batterijen hersteld, herhalingen beperkt, ongeldige gegevens gewist en prognosediagnose uitsluitend op debugniveau gelogd.",
12
+ "fr": "Correction des notifications de prévision pour consommateurs en pourcentage et batteries, limitation des doublons, suppression des données invalides et diagnostic uniquement au niveau debug.",
13
+ "it": "Corrette le notifiche di previsione per utenze percentuali e batterie, limitati i duplicati, eliminati i dati non validi e diagnostica solo a livello debug.",
14
+ "es": "Corregidas las notificaciones de previsión para consumidores porcentuales y baterías, limitados los duplicados, borrados los datos inválidos y diagnóstico solo en nivel debug.",
15
+ "pl": "Poprawiono powiadomienia prognozy dla odbiorników procentowych i akumulatorów, ograniczono duplikaty, usunięto nieprawidłowe dane; diagnostyka wyłącznie na poziomie debug.",
16
+ "uk": "Виправлено повідомлення прогнозу для відсоткових споживачів і батарей, обмежено повтори, очищено недійсні дані; діагностика прогнозу лише на рівні debug.",
17
+ "zh-cn": "修复百分比用电设备和电池的预测通知,限制重复通知,清除无效预测数据,所有预测诊断仅在 debug 级别记录。"
18
+ },
19
+ "0.2.0": {
20
+ "en": "Added sustained PV forecast control and minimum run/off times; fixed consumer commands, percent regulation, battery control, time windows and timers; updated translations, tests and workflows.",
21
+ "de": "PV-Prognosesteuerung und Mindestlaufzeiten ergänzt; Verbrauchersteuerung, Zeitfenster und Timer korrigiert; Übersetzungen, Tests und Workflows aktualisiert.",
22
+ "ru": "Добавлены управление по прогнозу ФЭ и минимальные времена работы; исправлены управление потребителями, временные окна и таймеры; обновлены переводы, тесты и рабочие процессы.",
23
+ "pt": "Adicionado controlo por previsão solar e tempos mínimos de funcionamento; corrigidos consumidores, horários e temporizadores; atualizados traduções, testes e fluxos de trabalho.",
24
+ "nl": "PV-prognosesturing en minimale aan/uit-tijden toegevoegd; schakelopdrachten, procentregeling, batterij, tijdvensters en timers hersteld; vertalingen, tests en workflows bijgewerkt.",
25
+ "fr": "Ajout des prévisions solaires et durées minimales ; correction des commandes, plages horaires et minuteries ; mise à jour des traductions, tests et workflows.",
26
+ "it": "Aggiunti controllo tramite previsioni FV e tempi minimi di accensione/spegnimento; corretti comandi, regolazione percentuale, batteria, fasce orarie e timer; aggiornati traduzioni, test e workflow.",
27
+ "es": "Añadidos control por previsión FV y tiempos mínimos; corregidos consumidores, horarios y temporizadores; actualizados traducciones, pruebas y flujos de trabajo.",
28
+ "pl": "Dodano prognozę PV i minimalne czasy pracy; poprawiono sterowanie odbiornikami, okna czasowe i zegary; zaktualizowano tłumaczenia, testy i przepływy pracy.",
29
+ "uk": "Додано керування за прогнозом ФЕ та мінімальні часи роботи; виправлено керування споживачами, часові вікна та таймери; оновлено переклади, тести та робочі процеси.",
30
+ "zh-cn": "新增光伏预测控制和最短运行时间;修复用电设备控制、时间窗口和定时器;更新翻译、测试和工作流。"
18
31
  },
19
- "0.1.1": {
32
+ "0.1.2": {
20
33
  "en": "Update dependencies",
21
34
  "de": "Abhängigkeiten aktualisieren",
22
35
  "ru": "Обновление зависимостей",
@@ -83,7 +96,7 @@
83
96
  },
84
97
  "0.0.1": {
85
98
  "en": "Release",
86
- "de": "Release",
99
+ "de": "Veröffentlichung",
87
100
  "ru": "Выпуск",
88
101
  "pt": "Lançar",
89
102
  "nl": "Loslaten",
@@ -163,7 +176,7 @@
163
176
  ],
164
177
  "globalDependencies": [
165
178
  {
166
- "admin": ">=7.6.17"
179
+ "admin": ">=7.6.20"
167
180
  }
168
181
  ]
169
182
  },
@@ -185,19 +198,46 @@
185
198
  },
186
199
  "description": {
187
200
  "en": "Notifications from SmartLoadManager",
188
- "de": "Benachrichtigungen vom SmartLoadManager"
201
+ "de": "Benachrichtigungen vom SmartLoadManager",
202
+ "ru": "Уведомления от SmartLoadManager",
203
+ "pt": "Notificações do SmartLoadManager",
204
+ "nl": "Meldingen van SmartLoadManager",
205
+ "fr": "Notifications de SmartLoadManager",
206
+ "it": "Notifiche da SmartLoadManager",
207
+ "es": "Notificaciones de SmartLoadManager",
208
+ "pl": "Powiadomienia z SmartLoadManager",
209
+ "uk": "Сповіщення від SmartLoadManager",
210
+ "zh-cn": "来自 SmartLoadManager 的通知"
189
211
  },
190
212
  "categories": [
191
213
  {
192
214
  "category": "info",
193
215
  "name": {
194
216
  "en": "Info",
195
- "de": "Info"
217
+ "de": "Information",
218
+ "ru": "Информация",
219
+ "pt": "Informação",
220
+ "nl": "Informatie",
221
+ "fr": "Information",
222
+ "it": "Informazioni",
223
+ "es": "Información",
224
+ "pl": "Informacja",
225
+ "uk": "Інформація",
226
+ "zh-cn": "信息"
196
227
  },
197
228
  "severity": "notify",
198
229
  "description": {
199
230
  "en": "Information about consumer switching or battery adjustments",
200
- "de": "Informationen zu Verbraucherschaltungen oder Batterieanpassungen"
231
+ "de": "Informationen zu Verbraucherschaltungen oder Batterieanpassungen",
232
+ "ru": "Информация о переключении потребителей или настройке батареи",
233
+ "pt": "Informações sobre comutação de consumidores ou ajustes da bateria",
234
+ "nl": "Informatie over het schakelen van verbruikers of batterijaanpassingen",
235
+ "fr": "Informations sur la commutation des consommateurs ou les réglages de batterie",
236
+ "it": "Informazioni sulla commutazione delle utenze o sulle regolazioni della batteria",
237
+ "es": "Información sobre la conmutación de consumidores o los ajustes de batería",
238
+ "pl": "Informacje o przełączaniu odbiorników lub regulacji baterii",
239
+ "uk": "Інформація про перемикання споживачів або налаштування батареї",
240
+ "zh-cn": "有关用电设备切换或电池调整的信息"
201
241
  },
202
242
  "regex": [],
203
243
  "limit": 1
@@ -206,12 +246,30 @@
206
246
  "category": "notify",
207
247
  "name": {
208
248
  "en": "Notify",
209
- "de": "Benachrichtigen"
249
+ "de": "Benachrichtigung",
250
+ "ru": "Уведомление",
251
+ "pt": "Notificação",
252
+ "nl": "Melding",
253
+ "fr": "Notification",
254
+ "it": "Notifica",
255
+ "es": "Notificación",
256
+ "pl": "Powiadomienie",
257
+ "uk": "Сповіщення",
258
+ "zh-cn": "通知"
210
259
  },
211
260
  "severity": "notify",
212
261
  "description": {
213
262
  "en": "Notify about events",
214
- "de": "Benachrichtigen über Ereignisse"
263
+ "de": "Über Ereignisse benachrichtigen",
264
+ "ru": "Уведомлять о событиях",
265
+ "pt": "Notificar sobre eventos",
266
+ "nl": "Melden over gebeurtenissen",
267
+ "fr": "Notifier les événements",
268
+ "it": "Notifica gli eventi",
269
+ "es": "Notificar sobre eventos",
270
+ "pl": "Powiadamiaj o zdarzeniach",
271
+ "uk": "Сповіщати про події",
272
+ "zh-cn": "事件通知"
215
273
  },
216
274
  "regex": [],
217
275
  "limit": 1
@@ -220,12 +278,30 @@
220
278
  "category": "alert",
221
279
  "name": {
222
280
  "en": "Alert",
223
- "de": "Alarm"
281
+ "de": "Alarm",
282
+ "ru": "Тревога",
283
+ "pt": "Alerta",
284
+ "nl": "Alarm",
285
+ "fr": "Alerte",
286
+ "it": "Allarme",
287
+ "es": "Alerta",
288
+ "pl": "Alarm",
289
+ "uk": "Тривога",
290
+ "zh-cn": "警报"
224
291
  },
225
292
  "severity": "alert",
226
293
  "description": {
227
294
  "en": "Alarm or failures in the adapter",
228
- "de": "Alarm oder Ausfälle im Adapter"
295
+ "de": "Alarm oder Ausfälle im Adapter",
296
+ "ru": "Тревоги или сбои адаптера",
297
+ "pt": "Alarmes ou falhas no adaptador",
298
+ "nl": "Alarmen of storingen in de adapter",
299
+ "fr": "Alarmes ou défaillances de l’adaptateur",
300
+ "it": "Allarmi o guasti nell'adattatore",
301
+ "es": "Alarmas o fallos en el adaptador",
302
+ "pl": "Alarmy lub awarie adaptera",
303
+ "uk": "Тривоги або збої адаптера",
304
+ "zh-cn": "适配器中的警报或故障"
229
305
  },
230
306
  "regex": [],
231
307
  "limit": 1
@@ -263,13 +339,19 @@
263
339
  "pushoverRecipients": "",
264
340
  "enableNotificationManager": false,
265
341
  "enableForecast": false,
266
- "forecastDefaultMinMinutes": 0,
342
+ "forecastDefaultMinMinutes": 30,
343
+ "forecastPowerUnit": "auto",
344
+ "forecastSafetyPercent": 80,
345
+ "forecastMaxStartMinutes": 30,
346
+ "forecastMinCurrentPercent": 50,
347
+ "forecastBridgeMinutes": 5,
348
+ "forecastMaxGridImport": 100,
267
349
  "enableNotifications": false,
268
350
  "notifyForecastBinary": false,
269
351
  "notifyForecastPercent": false,
270
352
  "notifyForecastBattery": false,
271
353
  "forecastJsonDatapoint": "",
272
- "notifyForecast": ""
354
+ "notifyForecast": false
273
355
  },
274
356
  "objects": [],
275
357
  "instanceObjects": []
@@ -0,0 +1,123 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Determine the conversion factor for forecast power values.
5
+ *
6
+ * @param {'W'|'kW'|null|undefined} unit Configured or detected power unit.
7
+ * @param {number[]} values Raw forecast values used for automatic detection.
8
+ * @returns {number} Multiplier to convert forecast values to watts.
9
+ */
10
+ function powerFactor(unit, values = []) {
11
+ if (unit === 'W') {
12
+ return 1;
13
+ }
14
+ if (unit === 'kW') {
15
+ return 1000;
16
+ }
17
+ const finite = values.filter(Number.isFinite).map(Math.abs);
18
+ return finite.length && Math.max(...finite) <= 100 ? 1000 : 1;
19
+ }
20
+
21
+ /**
22
+ * Parse pvforecast JSONData into normalized future data points.
23
+ *
24
+ * @param {string|Array<{t: number, y: number}>} value JSON string or decoded pvforecast array.
25
+ * @param {{now?: number, unit?: 'W'|'kW'|null, baseload?: number}} options Parser options.
26
+ * @returns {Array<{timestamp: number, power: number, surplus: number}>} Normalized forecast points.
27
+ */
28
+ function parseForecast(value, options = {}) {
29
+ const now = options.now || Date.now();
30
+ let data = value;
31
+ if (typeof data === 'string') {
32
+ data = JSON.parse(data);
33
+ }
34
+ if (!Array.isArray(data)) {
35
+ throw new Error('Forecast must be an array');
36
+ }
37
+
38
+ const raw = data
39
+ .map(entry => ({ timestamp: Number(entry?.t), value: Number(entry?.y) }))
40
+ .filter(entry => Number.isFinite(entry.timestamp) && Number.isFinite(entry.value) && entry.timestamp >= now)
41
+ .sort((a, b) => a.timestamp - b.timestamp);
42
+ const factor = powerFactor(
43
+ options.unit,
44
+ raw.map(entry => entry.value),
45
+ );
46
+ const baseload = Math.max(0, Number(options.baseload) || 0);
47
+ return raw.map(entry => ({
48
+ timestamp: entry.timestamp,
49
+ power: Math.max(0, Math.round(entry.value * factor)),
50
+ surplus: Math.max(0, Math.round(entry.value * factor - baseload)),
51
+ }));
52
+ }
53
+
54
+ /**
55
+ * Find a sustained forecast window with enough conservative surplus.
56
+ *
57
+ * @param {Array<{timestamp: number, surplus: number}>} points Normalized forecast points.
58
+ * @param {{now?: number, requiredPower?: number, safetyFactor?: number, minDurationMinutes?: number, maxStartMinutes?: number}} options Evaluation limits.
59
+ * @returns {{available: boolean, startMinutes: number|null, durationMinutes: number, minimumSurplus: number, averageSurplus: number}} Forecast decision.
60
+ */
61
+ function evaluateForecast(points, options = {}) {
62
+ const now = options.now || Date.now();
63
+ points = points
64
+ .filter(point => Number.isFinite(point.timestamp) && Number.isFinite(point.surplus) && point.timestamp >= now)
65
+ .sort((a, b) => a.timestamp - b.timestamp);
66
+ const requiredPower = Math.max(0, Number(options.requiredPower) || 0);
67
+ const safetyFactor = Math.min(1, Math.max(0.1, Number(options.safetyFactor) || 0.8));
68
+ const minDurationMinutes = Math.max(1, Number(options.minDurationMinutes) || 30);
69
+ const maxStartMinutes = Math.max(
70
+ 0,
71
+ options.maxStartMinutes === undefined ? 30 : Number(options.maxStartMinutes) || 0,
72
+ );
73
+ const threshold = requiredPower / safetyFactor;
74
+ const maxStart = now + maxStartMinutes * 60000;
75
+ const fallbackStep = 15 * 60000;
76
+
77
+ for (let start = 0; start < points.length; start++) {
78
+ if (points[start].timestamp > maxStart || points[start].surplus < threshold) {
79
+ continue;
80
+ }
81
+ let end = start;
82
+ let minimum = points[start].surplus;
83
+ let sum = 0;
84
+ while (end < points.length && points[end].surplus >= threshold) {
85
+ if (end > start) {
86
+ const gap = points[end].timestamp - points[end - 1].timestamp;
87
+ if (gap > 90 * 60000) {
88
+ break;
89
+ }
90
+ }
91
+ minimum = Math.min(minimum, points[end].surplus);
92
+ sum += points[end].surplus;
93
+ end++;
94
+ }
95
+ const step = end - start > 1 ? points[start + 1].timestamp - points[start].timestamp : fallbackStep;
96
+ const durationMinutes = Math.round((points[end - 1].timestamp - points[start].timestamp + step) / 60000);
97
+ if (durationMinutes >= minDurationMinutes) {
98
+ return {
99
+ available: true,
100
+ startMinutes: Math.max(0, Math.round((points[start].timestamp - now) / 60000)),
101
+ durationMinutes,
102
+ minimumSurplus: minimum,
103
+ averageSurplus: Math.round(sum / (end - start)),
104
+ };
105
+ }
106
+ }
107
+ return { available: false, startMinutes: null, durationMinutes: 0, minimumSurplus: 0, averageSurplus: 0 };
108
+ }
109
+
110
+ /**
111
+ * Check whether a minimum on/off period has elapsed.
112
+ *
113
+ * @param {number} lastChange Timestamp of the last state change.
114
+ * @param {number} minimumMinutes Required state duration in minutes.
115
+ * @param {number} now Current timestamp.
116
+ * @returns {boolean} Whether a new state change is allowed.
117
+ */
118
+ function minimumStateTimeSatisfied(lastChange, minimumMinutes, now = Date.now()) {
119
+ const minimumMs = Math.max(0, Number(minimumMinutes) || 0) * 60000;
120
+ return !lastChange || now - lastChange >= minimumMs;
121
+ }
122
+
123
+ module.exports = { evaluateForecast, minimumStateTimeSatisfied, parseForecast, powerFactor };