iobroker.poolcontrol 1.3.2 → 1.3.3
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 +5 -6
- package/io-package.json +14 -14
- package/lib/helpers/solarInsightsHelper.js +815 -0
- package/lib/helpers/solarLogbookHelper.js +616 -0
- package/lib/i18n/de.json +67 -1
- package/lib/i18n/en.json +67 -1
- package/lib/i18n/es.json +67 -1
- package/lib/i18n/fr.json +67 -1
- package/lib/i18n/it.json +67 -1
- package/lib/i18n/nl.json +67 -1
- package/lib/i18n/pl.json +67 -1
- package/lib/i18n/pt.json +67 -1
- package/lib/i18n/ru.json +67 -1
- package/lib/i18n/uk.json +67 -1
- package/lib/i18n/zh-cn.json +67 -1
- package/lib/stateDefinitions/solarInsightsStates.js +783 -0
- package/main.js +24 -0
- package/package.json +1 -1
|
@@ -0,0 +1,616 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const { I18n } = require('@iobroker/adapter-core');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* solarLogbookHelper
|
|
7
|
+
* - Creates human-readable solar logbook entries for the current day
|
|
8
|
+
* - Uses existing solar insights states only
|
|
9
|
+
* - Writes JSON + text + HTML logbook outputs
|
|
10
|
+
* - Event-based with debounce
|
|
11
|
+
* - Keeps user-facing texts human-friendly
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const solarLogbookHelper = {
|
|
15
|
+
adapter: null,
|
|
16
|
+
checkTimer: null,
|
|
17
|
+
resetTimer: null,
|
|
18
|
+
|
|
19
|
+
init(adapter) {
|
|
20
|
+
this.adapter = adapter;
|
|
21
|
+
|
|
22
|
+
void this._subscribeStates();
|
|
23
|
+
this._scheduleDailyReset();
|
|
24
|
+
this._scheduleCheck(0);
|
|
25
|
+
|
|
26
|
+
this.adapter.log.debug('[solarLogbookHelper] Initialized');
|
|
27
|
+
},
|
|
28
|
+
|
|
29
|
+
handleStateChange(id, state) {
|
|
30
|
+
if (!state || state.ack !== true) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (!this._isRelevantState(id)) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
this._scheduleCheck(250);
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
_scheduleCheck(delayMs = 0) {
|
|
42
|
+
if (this.checkTimer) {
|
|
43
|
+
this.adapter.clearTimeout(this.checkTimer);
|
|
44
|
+
this.checkTimer = null;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
this.checkTimer = this.adapter.setTimeout(() => {
|
|
48
|
+
this.checkTimer = null;
|
|
49
|
+
void this._updateLogbook();
|
|
50
|
+
}, delayMs);
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
_scheduleDailyReset() {
|
|
54
|
+
if (this.resetTimer) {
|
|
55
|
+
this.adapter.clearTimeout(this.resetTimer);
|
|
56
|
+
this.resetTimer = null;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const now = new Date();
|
|
60
|
+
const next = new Date(now);
|
|
61
|
+
next.setDate(now.getDate() + 1);
|
|
62
|
+
next.setHours(0, 0, 10, 0);
|
|
63
|
+
|
|
64
|
+
const delay = Math.max(1000, next.getTime() - now.getTime());
|
|
65
|
+
|
|
66
|
+
this.resetTimer = this.adapter.setTimeout(async () => {
|
|
67
|
+
try {
|
|
68
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.current_entry', {
|
|
69
|
+
val: '',
|
|
70
|
+
ack: true,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.current_entry_html', {
|
|
74
|
+
val: '',
|
|
75
|
+
ack: true,
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.day_log_json', {
|
|
79
|
+
val: '[]',
|
|
80
|
+
ack: true,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.day_log_text', {
|
|
84
|
+
val: '',
|
|
85
|
+
ack: true,
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.last_entry_time', {
|
|
89
|
+
val: '',
|
|
90
|
+
ack: true,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
this.adapter.log.debug('[solarLogbookHelper] Daily reset executed');
|
|
94
|
+
} catch (err) {
|
|
95
|
+
this.adapter.log.warn(`[solarLogbookHelper] Daily reset failed: ${err.message}`);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
this._scheduleDailyReset();
|
|
99
|
+
}, delay);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
async _subscribeStates() {
|
|
103
|
+
const ids = [
|
|
104
|
+
'analytics.insights.solar.results.solar_ran_today',
|
|
105
|
+
'analytics.insights.solar.results.solar_effective_now',
|
|
106
|
+
'analytics.insights.solar.results.solar_gain_state',
|
|
107
|
+
'analytics.insights.solar.results.collector_temp_used',
|
|
108
|
+
'analytics.insights.solar.results.pool_reference_temp_used',
|
|
109
|
+
'analytics.insights.solar.results.delta_t_used',
|
|
110
|
+
'analytics.insights.solar.results.outside_temp_used',
|
|
111
|
+
'analytics.insights.solar.results.flow_lh_used',
|
|
112
|
+
'analytics.insights.solar.results.pump_power_w_used',
|
|
113
|
+
'analytics.insights.solar.results.estimated_thermal_power_w',
|
|
114
|
+
'analytics.insights.solar.results.estimated_thermal_power_kw',
|
|
115
|
+
'analytics.insights.solar.results.estimated_efficiency_ratio',
|
|
116
|
+
'analytics.insights.solar.results.estimated_gain_today_wh',
|
|
117
|
+
'analytics.insights.solar.results.estimated_gain_today_kwh',
|
|
118
|
+
'analytics.insights.solar.results.active_minutes_today',
|
|
119
|
+
'analytics.insights.solar.results.peak_power_today_w',
|
|
120
|
+
|
|
121
|
+
'analytics.insights.solar.calculation.quality_level',
|
|
122
|
+
'analytics.insights.solar.calculation.confidence_percent',
|
|
123
|
+
'analytics.insights.solar.calculation.weather_correction_active',
|
|
124
|
+
|
|
125
|
+
'analytics.insights.solar.inputs.used_sensors_text',
|
|
126
|
+
|
|
127
|
+
'ai.weather.outputs.daily_summary',
|
|
128
|
+
];
|
|
129
|
+
|
|
130
|
+
for (const id of ids) {
|
|
131
|
+
await this.adapter.subscribeStatesAsync(id);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
this.adapter.log.debug('[solarLogbookHelper] Relevant states subscribed');
|
|
135
|
+
},
|
|
136
|
+
|
|
137
|
+
_isRelevantState(id) {
|
|
138
|
+
const ids = [
|
|
139
|
+
'analytics.insights.solar.results.solar_ran_today',
|
|
140
|
+
'analytics.insights.solar.results.solar_effective_now',
|
|
141
|
+
'analytics.insights.solar.results.solar_gain_state',
|
|
142
|
+
'analytics.insights.solar.results.collector_temp_used',
|
|
143
|
+
'analytics.insights.solar.results.pool_reference_temp_used',
|
|
144
|
+
'analytics.insights.solar.results.delta_t_used',
|
|
145
|
+
'analytics.insights.solar.results.outside_temp_used',
|
|
146
|
+
'analytics.insights.solar.results.flow_lh_used',
|
|
147
|
+
'analytics.insights.solar.results.pump_power_w_used',
|
|
148
|
+
'analytics.insights.solar.results.estimated_thermal_power_w',
|
|
149
|
+
'analytics.insights.solar.results.estimated_thermal_power_kw',
|
|
150
|
+
'analytics.insights.solar.results.estimated_efficiency_ratio',
|
|
151
|
+
'analytics.insights.solar.results.estimated_gain_today_wh',
|
|
152
|
+
'analytics.insights.solar.results.estimated_gain_today_kwh',
|
|
153
|
+
'analytics.insights.solar.results.active_minutes_today',
|
|
154
|
+
'analytics.insights.solar.results.peak_power_today_w',
|
|
155
|
+
|
|
156
|
+
'analytics.insights.solar.calculation.quality_level',
|
|
157
|
+
'analytics.insights.solar.calculation.confidence_percent',
|
|
158
|
+
'analytics.insights.solar.calculation.weather_correction_active',
|
|
159
|
+
|
|
160
|
+
'analytics.insights.solar.inputs.used_sensors_text',
|
|
161
|
+
|
|
162
|
+
'ai.weather.outputs.daily_summary',
|
|
163
|
+
];
|
|
164
|
+
|
|
165
|
+
return ids.some(relevantId => id === relevantId || id.endsWith(`.${relevantId}`));
|
|
166
|
+
},
|
|
167
|
+
|
|
168
|
+
async _updateLogbook() {
|
|
169
|
+
try {
|
|
170
|
+
const solarRanToday = await this._readBoolean('analytics.insights.solar.results.solar_ran_today');
|
|
171
|
+
const solarEffectiveNow = await this._readBoolean('analytics.insights.solar.results.solar_effective_now');
|
|
172
|
+
|
|
173
|
+
const collectorTemp = await this._readNumber('analytics.insights.solar.results.collector_temp_used');
|
|
174
|
+
const poolReferenceTemp = await this._readNumber(
|
|
175
|
+
'analytics.insights.solar.results.pool_reference_temp_used',
|
|
176
|
+
);
|
|
177
|
+
const deltaTUsed = await this._readNumber('analytics.insights.solar.results.delta_t_used');
|
|
178
|
+
const outsideTemp = await this._readNumber('analytics.insights.solar.results.outside_temp_used');
|
|
179
|
+
const flowLhUsed = await this._readNumber('analytics.insights.solar.results.flow_lh_used');
|
|
180
|
+
const pumpPowerWUsed = await this._readNumber('analytics.insights.solar.results.pump_power_w_used');
|
|
181
|
+
const thermalPowerW = await this._readNumber('analytics.insights.solar.results.estimated_thermal_power_w');
|
|
182
|
+
const thermalPowerKW = await this._readNumber(
|
|
183
|
+
'analytics.insights.solar.results.estimated_thermal_power_kw',
|
|
184
|
+
);
|
|
185
|
+
const efficiencyRatio = await this._readNumber(
|
|
186
|
+
'analytics.insights.solar.results.estimated_efficiency_ratio',
|
|
187
|
+
);
|
|
188
|
+
const gainTodayWh = await this._readNumber('analytics.insights.solar.results.estimated_gain_today_wh');
|
|
189
|
+
const gainTodayKWh = await this._readNumber('analytics.insights.solar.results.estimated_gain_today_kwh');
|
|
190
|
+
const activeMinutesToday = await this._readNumber('analytics.insights.solar.results.active_minutes_today');
|
|
191
|
+
const peakPowerTodayW = await this._readNumber('analytics.insights.solar.results.peak_power_today_w');
|
|
192
|
+
|
|
193
|
+
const qualityLevel = await this._readString('analytics.insights.solar.calculation.quality_level');
|
|
194
|
+
const confidencePercent = await this._readNumber('analytics.insights.solar.calculation.confidence_percent');
|
|
195
|
+
const weatherCorrectionActive = await this._readBoolean(
|
|
196
|
+
'analytics.insights.solar.calculation.weather_correction_active',
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
const usedSensorsText = await this._readString('analytics.insights.solar.inputs.used_sensors_text');
|
|
200
|
+
const weatherSummary = await this._readString('ai.weather.outputs.daily_summary');
|
|
201
|
+
|
|
202
|
+
const entry = this._buildHumanEntry({
|
|
203
|
+
solarRanToday,
|
|
204
|
+
solarEffectiveNow,
|
|
205
|
+
collectorTemp,
|
|
206
|
+
poolReferenceTemp,
|
|
207
|
+
deltaTUsed,
|
|
208
|
+
outsideTemp,
|
|
209
|
+
flowLhUsed,
|
|
210
|
+
pumpPowerWUsed,
|
|
211
|
+
thermalPowerW,
|
|
212
|
+
thermalPowerKW,
|
|
213
|
+
efficiencyRatio,
|
|
214
|
+
gainTodayWh,
|
|
215
|
+
gainTodayKWh,
|
|
216
|
+
activeMinutesToday,
|
|
217
|
+
peakPowerTodayW,
|
|
218
|
+
qualityLevel,
|
|
219
|
+
confidencePercent,
|
|
220
|
+
weatherCorrectionActive,
|
|
221
|
+
usedSensorsText,
|
|
222
|
+
weatherSummary,
|
|
223
|
+
});
|
|
224
|
+
|
|
225
|
+
const currentEntry = await this._readString('analytics.insights.solar.logbook.current_entry');
|
|
226
|
+
|
|
227
|
+
if (currentEntry === entry.text) {
|
|
228
|
+
return;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const now = new Date();
|
|
232
|
+
const nowIso = now.toISOString();
|
|
233
|
+
const timeLabel = this._formatTime(now);
|
|
234
|
+
|
|
235
|
+
const newLogItem = {
|
|
236
|
+
ts: nowIso,
|
|
237
|
+
time: timeLabel,
|
|
238
|
+
type: entry.type,
|
|
239
|
+
text: entry.text,
|
|
240
|
+
html: entry.html,
|
|
241
|
+
};
|
|
242
|
+
|
|
243
|
+
const dayLogJsonRaw = await this._readString('analytics.insights.solar.logbook.day_log_json');
|
|
244
|
+
const dayLog = this._safeParseArray(dayLogJsonRaw);
|
|
245
|
+
|
|
246
|
+
dayLog.push(newLogItem);
|
|
247
|
+
|
|
248
|
+
const trimmedLog = dayLog.slice(-100);
|
|
249
|
+
|
|
250
|
+
const dayLogText = trimmedLog.map(item => `${item.time} - ${item.text}`).join('\n');
|
|
251
|
+
|
|
252
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.current_entry', {
|
|
253
|
+
val: entry.text,
|
|
254
|
+
ack: true,
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.current_entry_html', {
|
|
258
|
+
val: entry.html,
|
|
259
|
+
ack: true,
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.day_log_json', {
|
|
263
|
+
val: JSON.stringify(trimmedLog),
|
|
264
|
+
ack: true,
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.day_log_text', {
|
|
268
|
+
val: dayLogText,
|
|
269
|
+
ack: true,
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
await this.adapter.setStateChangedAsync('analytics.insights.solar.logbook.last_entry_time', {
|
|
273
|
+
val: nowIso,
|
|
274
|
+
ack: true,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
this.adapter.log.debug('[solarLogbookHelper] Logbook updated successfully');
|
|
278
|
+
} catch (err) {
|
|
279
|
+
this.adapter.log.warn(`[solarLogbookHelper] Error in update: ${err.message}`);
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
|
|
283
|
+
_buildHumanEntry(data) {
|
|
284
|
+
const {
|
|
285
|
+
solarRanToday,
|
|
286
|
+
solarEffectiveNow,
|
|
287
|
+
collectorTemp,
|
|
288
|
+
poolReferenceTemp,
|
|
289
|
+
deltaTUsed,
|
|
290
|
+
outsideTemp,
|
|
291
|
+
flowLhUsed,
|
|
292
|
+
pumpPowerWUsed,
|
|
293
|
+
thermalPowerW,
|
|
294
|
+
thermalPowerKW,
|
|
295
|
+
efficiencyRatio,
|
|
296
|
+
gainTodayWh,
|
|
297
|
+
gainTodayKWh,
|
|
298
|
+
activeMinutesToday,
|
|
299
|
+
peakPowerTodayW,
|
|
300
|
+
qualityLevel,
|
|
301
|
+
confidencePercent,
|
|
302
|
+
weatherCorrectionActive,
|
|
303
|
+
usedSensorsText,
|
|
304
|
+
weatherSummary,
|
|
305
|
+
} = data;
|
|
306
|
+
|
|
307
|
+
const sentences = [];
|
|
308
|
+
const usedSensors = usedSensorsText || this._t('solar_log_value_unknown');
|
|
309
|
+
const confidenceText = Number.isFinite(confidencePercent)
|
|
310
|
+
? this._tf('solar_log_confidence_text', this._formatNumber(confidencePercent, 0))
|
|
311
|
+
: '';
|
|
312
|
+
const weatherTextShort = this._extractShortWeather(weatherSummary);
|
|
313
|
+
|
|
314
|
+
if (!solarRanToday) {
|
|
315
|
+
sentences.push(this._t('solar_log_text_no_runtime_today'));
|
|
316
|
+
|
|
317
|
+
if (weatherTextShort !== '') {
|
|
318
|
+
sentences.push(this._tf('solar_log_text_weather_short', weatherTextShort));
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
if (confidenceText !== '') {
|
|
322
|
+
sentences.push(confidenceText);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
sentences.push(this._tf('solar_log_text_used_sensors', usedSensors));
|
|
326
|
+
|
|
327
|
+
const text = sentences.join(' ');
|
|
328
|
+
return {
|
|
329
|
+
type: 'no_runtime_today',
|
|
330
|
+
text,
|
|
331
|
+
html: this._escapeHtml(text),
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (solarEffectiveNow && Number.isFinite(thermalPowerKW) && thermalPowerKW > 0) {
|
|
336
|
+
sentences.push(this._tf('solar_log_text_active_effective', this._formatNumber(thermalPowerKW, 2)));
|
|
337
|
+
} else if (solarEffectiveNow) {
|
|
338
|
+
sentences.push(this._t('solar_log_text_active_without_clear_gain'));
|
|
339
|
+
} else {
|
|
340
|
+
sentences.push(this._t('solar_log_text_not_active_now'));
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
if (Number.isFinite(gainTodayKWh) && gainTodayKWh >= 3) {
|
|
344
|
+
sentences.push(this._tf('solar_log_text_good_day', this._formatNumber(gainTodayKWh, 2)));
|
|
345
|
+
} else if (Number.isFinite(gainTodayKWh) && gainTodayKWh >= 1) {
|
|
346
|
+
sentences.push(this._tf('solar_log_text_moderate_day', this._formatNumber(gainTodayKWh, 2)));
|
|
347
|
+
} else if (Number.isFinite(gainTodayKWh) && gainTodayKWh > 0) {
|
|
348
|
+
sentences.push(this._tf('solar_log_text_low_day', this._formatNumber(gainTodayKWh, 2)));
|
|
349
|
+
} else {
|
|
350
|
+
sentences.push(this._t('solar_log_text_no_clear_day_gain'));
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
if (Number.isFinite(collectorTemp) && Number.isFinite(poolReferenceTemp) && Number.isFinite(deltaTUsed)) {
|
|
354
|
+
if (deltaTUsed > 0.5) {
|
|
355
|
+
sentences.push(
|
|
356
|
+
this._tf(
|
|
357
|
+
'solar_log_text_temperature_positive',
|
|
358
|
+
this._formatNumber(collectorTemp, 1),
|
|
359
|
+
this._formatNumber(poolReferenceTemp, 1),
|
|
360
|
+
this._formatNumber(deltaTUsed, 1),
|
|
361
|
+
),
|
|
362
|
+
);
|
|
363
|
+
} else if (deltaTUsed <= 0.5 && deltaTUsed >= -0.5) {
|
|
364
|
+
sentences.push(
|
|
365
|
+
this._tf(
|
|
366
|
+
'solar_log_text_temperature_neutral',
|
|
367
|
+
this._formatNumber(collectorTemp, 1),
|
|
368
|
+
this._formatNumber(poolReferenceTemp, 1),
|
|
369
|
+
),
|
|
370
|
+
);
|
|
371
|
+
} else {
|
|
372
|
+
sentences.push(
|
|
373
|
+
this._tf(
|
|
374
|
+
'solar_log_text_temperature_negative',
|
|
375
|
+
this._formatNumber(collectorTemp, 1),
|
|
376
|
+
this._formatNumber(poolReferenceTemp, 1),
|
|
377
|
+
),
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
if (Number.isFinite(flowLhUsed) && flowLhUsed > 0 && Number.isFinite(pumpPowerWUsed) && pumpPowerWUsed > 0) {
|
|
383
|
+
sentences.push(
|
|
384
|
+
this._tf(
|
|
385
|
+
'solar_log_text_flow_and_pump',
|
|
386
|
+
this._formatNumber(flowLhUsed, 0),
|
|
387
|
+
this._formatNumber(pumpPowerWUsed, 0),
|
|
388
|
+
),
|
|
389
|
+
);
|
|
390
|
+
} else if (Number.isFinite(flowLhUsed) && flowLhUsed > 0) {
|
|
391
|
+
sentences.push(this._tf('solar_log_text_flow_only', this._formatNumber(flowLhUsed, 0)));
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
if (
|
|
395
|
+
Number.isFinite(thermalPowerW) &&
|
|
396
|
+
thermalPowerW > 0 &&
|
|
397
|
+
Number.isFinite(peakPowerTodayW) &&
|
|
398
|
+
peakPowerTodayW > 0
|
|
399
|
+
) {
|
|
400
|
+
sentences.push(
|
|
401
|
+
this._tf(
|
|
402
|
+
'solar_log_text_power_and_peak',
|
|
403
|
+
this._formatNumber(thermalPowerW, 0),
|
|
404
|
+
this._formatNumber(peakPowerTodayW, 0),
|
|
405
|
+
),
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
if (Number.isFinite(efficiencyRatio) && efficiencyRatio > 0) {
|
|
410
|
+
if (efficiencyRatio >= 5) {
|
|
411
|
+
sentences.push(this._tf('solar_log_text_efficiency_high', this._formatNumber(efficiencyRatio, 2)));
|
|
412
|
+
} else if (efficiencyRatio >= 2) {
|
|
413
|
+
sentences.push(this._tf('solar_log_text_efficiency_medium', this._formatNumber(efficiencyRatio, 2)));
|
|
414
|
+
} else {
|
|
415
|
+
sentences.push(this._tf('solar_log_text_efficiency_low', this._formatNumber(efficiencyRatio, 2)));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
if (Number.isFinite(activeMinutesToday) && activeMinutesToday > 0) {
|
|
420
|
+
sentences.push(this._tf('solar_log_text_active_minutes', this._formatNumber(activeMinutesToday, 0)));
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
if (Number.isFinite(outsideTemp)) {
|
|
424
|
+
sentences.push(this._tf('solar_log_text_outside_temp', this._formatNumber(outsideTemp, 1)));
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
if (weatherCorrectionActive) {
|
|
428
|
+
if (weatherTextShort !== '') {
|
|
429
|
+
sentences.push(this._tf('solar_log_text_weather_supported_with_text', weatherTextShort));
|
|
430
|
+
} else {
|
|
431
|
+
sentences.push(this._t('solar_log_text_weather_supported'));
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
if (Number.isFinite(gainTodayWh) && gainTodayWh > 0 && gainTodayWh < 1000 && Number.isFinite(gainTodayKWh)) {
|
|
436
|
+
sentences.push(
|
|
437
|
+
this._tf(
|
|
438
|
+
'solar_log_text_small_gain_detail',
|
|
439
|
+
this._formatNumber(gainTodayWh, 0),
|
|
440
|
+
this._formatNumber(gainTodayKWh, 2),
|
|
441
|
+
),
|
|
442
|
+
);
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (qualityLevel !== '') {
|
|
446
|
+
sentences.push(this._tf('solar_log_text_quality_level', qualityLevel));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
if (confidenceText !== '') {
|
|
450
|
+
sentences.push(confidenceText);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
sentences.push(this._tf('solar_log_text_used_sensors', usedSensors));
|
|
454
|
+
|
|
455
|
+
const text = sentences.join(' ');
|
|
456
|
+
return {
|
|
457
|
+
type: solarEffectiveNow ? 'active_log_entry' : 'daily_log_entry',
|
|
458
|
+
text,
|
|
459
|
+
html: this._escapeHtml(text),
|
|
460
|
+
};
|
|
461
|
+
},
|
|
462
|
+
|
|
463
|
+
_extractShortWeather(weatherSummary) {
|
|
464
|
+
if (!weatherSummary || weatherSummary.trim() === '') {
|
|
465
|
+
return '';
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
const text = weatherSummary.replace(/\s+/g, ' ').trim();
|
|
469
|
+
|
|
470
|
+
if (text.length <= 140) {
|
|
471
|
+
return text;
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return `${text.slice(0, 137)}...`;
|
|
475
|
+
},
|
|
476
|
+
|
|
477
|
+
async _readState(id) {
|
|
478
|
+
try {
|
|
479
|
+
return await this.adapter.getStateAsync(id);
|
|
480
|
+
} catch {
|
|
481
|
+
return null;
|
|
482
|
+
}
|
|
483
|
+
},
|
|
484
|
+
|
|
485
|
+
async _readBoolean(id) {
|
|
486
|
+
const state = await this._readState(id);
|
|
487
|
+
|
|
488
|
+
if (!state) {
|
|
489
|
+
return false;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
return state.val === true;
|
|
493
|
+
},
|
|
494
|
+
|
|
495
|
+
async _readNumber(id) {
|
|
496
|
+
const state = await this._readState(id);
|
|
497
|
+
|
|
498
|
+
if (!state || state.val === null || state.val === undefined || state.val === '') {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
const value = Number(state.val);
|
|
503
|
+
return Number.isFinite(value) ? value : null;
|
|
504
|
+
},
|
|
505
|
+
|
|
506
|
+
async _readString(id) {
|
|
507
|
+
const state = await this._readState(id);
|
|
508
|
+
|
|
509
|
+
if (!state || state.val === null || state.val === undefined) {
|
|
510
|
+
return '';
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
return String(state.val);
|
|
514
|
+
},
|
|
515
|
+
|
|
516
|
+
_safeParseArray(jsonText) {
|
|
517
|
+
if (!jsonText || jsonText.trim() === '') {
|
|
518
|
+
return [];
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
const parsed = JSON.parse(jsonText);
|
|
523
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
524
|
+
} catch {
|
|
525
|
+
return [];
|
|
526
|
+
}
|
|
527
|
+
},
|
|
528
|
+
|
|
529
|
+
_formatNumber(value, digits = 1) {
|
|
530
|
+
if (!Number.isFinite(value)) {
|
|
531
|
+
return '';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
return Number(value).toFixed(digits);
|
|
535
|
+
},
|
|
536
|
+
|
|
537
|
+
_formatTime(date) {
|
|
538
|
+
const hours = String(date.getHours()).padStart(2, '0');
|
|
539
|
+
const minutes = String(date.getMinutes()).padStart(2, '0');
|
|
540
|
+
return `${hours}:${minutes}`;
|
|
541
|
+
},
|
|
542
|
+
|
|
543
|
+
_escapeHtml(text) {
|
|
544
|
+
return String(text)
|
|
545
|
+
.replace(/&/g, '&')
|
|
546
|
+
.replace(/</g, '<')
|
|
547
|
+
.replace(/>/g, '>')
|
|
548
|
+
.replace(/"/g, '"')
|
|
549
|
+
.replace(/'/g, ''');
|
|
550
|
+
},
|
|
551
|
+
|
|
552
|
+
_t(key) {
|
|
553
|
+
try {
|
|
554
|
+
return I18n.t(key);
|
|
555
|
+
} catch {
|
|
556
|
+
return key;
|
|
557
|
+
}
|
|
558
|
+
},
|
|
559
|
+
|
|
560
|
+
_tf(key, ...args) {
|
|
561
|
+
try {
|
|
562
|
+
let text = I18n.t(key);
|
|
563
|
+
|
|
564
|
+
for (const arg of args) {
|
|
565
|
+
text = text.replace('%s', String(arg));
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
return text;
|
|
569
|
+
} catch {
|
|
570
|
+
return key;
|
|
571
|
+
}
|
|
572
|
+
},
|
|
573
|
+
|
|
574
|
+
cleanup() {
|
|
575
|
+
if (this.checkTimer) {
|
|
576
|
+
this.adapter.clearTimeout(this.checkTimer);
|
|
577
|
+
this.checkTimer = null;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
if (this.resetTimer) {
|
|
581
|
+
this.adapter.clearTimeout(this.resetTimer);
|
|
582
|
+
this.resetTimer = null;
|
|
583
|
+
}
|
|
584
|
+
},
|
|
585
|
+
};
|
|
586
|
+
|
|
587
|
+
module.exports = solarLogbookHelper;
|
|
588
|
+
|
|
589
|
+
// i18n keys required:
|
|
590
|
+
// solar_log_value_unknown
|
|
591
|
+
// solar_log_confidence_text
|
|
592
|
+
// solar_log_text_no_runtime_today
|
|
593
|
+
// solar_log_text_weather_short
|
|
594
|
+
// solar_log_text_used_sensors
|
|
595
|
+
// solar_log_text_active_effective
|
|
596
|
+
// solar_log_text_active_without_clear_gain
|
|
597
|
+
// solar_log_text_not_active_now
|
|
598
|
+
// solar_log_text_good_day
|
|
599
|
+
// solar_log_text_moderate_day
|
|
600
|
+
// solar_log_text_low_day
|
|
601
|
+
// solar_log_text_no_clear_day_gain
|
|
602
|
+
// solar_log_text_temperature_positive
|
|
603
|
+
// solar_log_text_temperature_neutral
|
|
604
|
+
// solar_log_text_temperature_negative
|
|
605
|
+
// solar_log_text_flow_and_pump
|
|
606
|
+
// solar_log_text_flow_only
|
|
607
|
+
// solar_log_text_power_and_peak
|
|
608
|
+
// solar_log_text_efficiency_high
|
|
609
|
+
// solar_log_text_efficiency_medium
|
|
610
|
+
// solar_log_text_efficiency_low
|
|
611
|
+
// solar_log_text_active_minutes
|
|
612
|
+
// solar_log_text_outside_temp
|
|
613
|
+
// solar_log_text_weather_supported_with_text
|
|
614
|
+
// solar_log_text_weather_supported
|
|
615
|
+
// solar_log_text_small_gain_detail
|
|
616
|
+
// solar_log_text_quality_level
|
package/lib/i18n/de.json
CHANGED
|
@@ -101,5 +101,71 @@
|
|
|
101
101
|
"extended waiting for temperatures": "Extended wartet auf Temperaturen",
|
|
102
102
|
"extended blocked by actor write error": "Extended durch Aktor-Schreibfehler blockiert",
|
|
103
103
|
"extended active": "Extended aktiv",
|
|
104
|
-
"extended inactive": "Extended inaktiv"
|
|
104
|
+
"extended inactive": "Extended inaktiv",
|
|
105
|
+
|
|
106
|
+
"solar_insights_summary_note_block_7": "Block 7 schreibt direkte Referenzwerte, geschätzte thermische Leistung, geschätzten Tagesertrag, geschätztes Effizienzverhältnis, die Nutzung des Rücklaufsensors sowie wetterbasierte Bewertungskennzeichen.",
|
|
107
|
+
"solar_insights_label_mode": "Modus",
|
|
108
|
+
"solar_insights_label_solar_ran_today": "Solar lief heute",
|
|
109
|
+
"solar_insights_label_solar_effective_now": "Solar aktuell wirksam",
|
|
110
|
+
"solar_insights_label_pool_reference_source": "Pool-Referenzquelle",
|
|
111
|
+
"solar_insights_label_flow_source": "Durchflussquelle",
|
|
112
|
+
"solar_insights_label_weather_correction_active": "Wetterkorrektur aktiv",
|
|
113
|
+
"solar_insights_label_confidence": "Vertrauen",
|
|
114
|
+
"solar_insights_label_used_sensors": "Verwendete Sensoren",
|
|
115
|
+
"solar_insights_label_collector": "Kollektor",
|
|
116
|
+
"solar_insights_label_pool_reference": "Pool-Referenz",
|
|
117
|
+
"solar_insights_label_delta_t": "Delta T",
|
|
118
|
+
"solar_insights_label_surface_ground_delta": "Differenz Oberfläche-Boden",
|
|
119
|
+
"solar_insights_label_outside": "Außen",
|
|
120
|
+
"solar_insights_label_flow": "Durchfluss",
|
|
121
|
+
"solar_insights_label_pump_power": "Pumpenleistung",
|
|
122
|
+
"solar_insights_label_thermal_power": "Thermische Leistung",
|
|
123
|
+
"solar_insights_label_estimated_efficiency_ratio": "Geschätztes Effizienzverhältnis",
|
|
124
|
+
"solar_insights_label_gain_today": "Tagesertrag",
|
|
125
|
+
"solar_insights_label_active_minutes_today": "Aktive Minuten heute",
|
|
126
|
+
"solar_insights_label_peak_power_today": "Spitzenleistung heute",
|
|
127
|
+
"solar_insights_label_note": "Hinweis",
|
|
128
|
+
"solar_insights_value_none": "keine",
|
|
129
|
+
"solar_insights_value_na": "n/v",
|
|
130
|
+
"solar_insights_reason_daily_reset": "täglicher Reset",
|
|
131
|
+
"solar_insights_debug_daily_reset_executed": "Solar-Insights Tagesreset ausgeführt.",
|
|
132
|
+
"solar_insights_status_estimated_daily_gain_ready": "estimated_daily_gain_ready",
|
|
133
|
+
"solar_insights_status_no_solar_runtime_today": "no_solar_runtime_today",
|
|
134
|
+
"solar_insights_mode_estimated_daily_gain": "estimated_daily_gain",
|
|
135
|
+
"solar_insights_quality_advanced": "advanced",
|
|
136
|
+
"solar_insights_quality_enhanced": "enhanced",
|
|
137
|
+
"solar_insights_quality_basic": "basic",
|
|
138
|
+
"solar_insights_calculation_note_block_7": "Solar-Insights Block 7 aktiv: direkte Referenzwerte, geschätzte thermische Leistung, geschätzter Tagesertrag, geschätztes Effizienzverhältnis und wetterbasierte Bewertungskennzeichen werden geschrieben.",
|
|
139
|
+
"solar_insights_reason_block_7_update": "Block 7 Aktualisierung",
|
|
140
|
+
"solar_insights_debug_block_7_updated": "Solar-Insights Block 7 aktualisiert. Verwendete Sensoren:",
|
|
141
|
+
"solar_insights_debug_block_7_error": "Solar-Insights Block 7 Fehler:",
|
|
142
|
+
"solar_insights_html_note_block_7": "Geschätzter Tagesertrag, geschätztes Effizienzverhältnis, Nutzung des Rücklaufsensors und wetterbasierte Bewertungskennzeichen sind aktiv.",
|
|
143
|
+
|
|
144
|
+
"solar_log_value_unknown": "unbekannt",
|
|
145
|
+
"solar_log_confidence_text": "Die aktuelle Vertrauensstufe dieser Bewertung liegt bei etwa %s Prozent.",
|
|
146
|
+
"solar_log_text_no_runtime_today": "Solar ist heute nicht gelaufen, daher kann kein sinnvoller Solarertrag für den Tag angegeben werden.",
|
|
147
|
+
"solar_log_text_weather_short": "Wetterkontext: %s",
|
|
148
|
+
"solar_log_text_used_sensors": "Die aktuelle Bewertung basiert auf folgenden Sensoren und Datenquellen: %s.",
|
|
149
|
+
"solar_log_text_active_effective": "Solar arbeitet aktuell effektiv und liefert etwa %s kW thermische Leistung.",
|
|
150
|
+
"solar_log_text_active_without_clear_gain": "Solar ist aktuell aktiv, es lässt sich jedoch momentan kein klar nutzbarer Wärmegewinn erkennen.",
|
|
151
|
+
"solar_log_text_not_active_now": "Solar trägt aktuell nicht aktiv zur Poolerwärmung bei.",
|
|
152
|
+
"solar_log_text_good_day": "Heute hat Solar bereits einen starken Beitrag geleistet, mit einem geschätzten Wärmegewinn von etwa %s kWh.",
|
|
153
|
+
"solar_log_text_moderate_day": "Heute hat Solar bereits einen merkbaren Wärmegewinn von etwa %s kWh geliefert.",
|
|
154
|
+
"solar_log_text_low_day": "Heute hat Solar bisher nur einen geringen geschätzten Wärmegewinn von etwa %s kWh geliefert.",
|
|
155
|
+
"solar_log_text_no_clear_day_gain": "Bisher lässt sich aus den verfügbaren Daten kein klar nutzbarer täglicher Solarertrag ableiten.",
|
|
156
|
+
"solar_log_text_temperature_positive": "Der Kollektor liegt aktuell bei etwa %s °C, die Pool-Referenz bei etwa %s °C, und der Temperaturvorteil beträgt etwa %s K.",
|
|
157
|
+
"solar_log_text_temperature_neutral": "Die Kollektortemperatur von etwa %s °C liegt aktuell sehr nahe an der Pool-Referenztemperatur von etwa %s °C.",
|
|
158
|
+
"solar_log_text_temperature_negative": "Die Kollektortemperatur von etwa %s °C liegt aktuell unter der Pool-Referenztemperatur von etwa %s °C.",
|
|
159
|
+
"solar_log_text_flow_and_pump": "Der aktuell geschätzte Durchfluss liegt bei etwa %s l/h bei einer Pumpenleistung von etwa %s W.",
|
|
160
|
+
"solar_log_text_flow_only": "Der aktuell geschätzte Durchfluss liegt bei etwa %s l/h.",
|
|
161
|
+
"solar_log_text_power_and_peak": "Die aktuell geschätzte thermische Leistung liegt bei etwa %s W, mit einer heutigen Spitzenleistung von etwa %s W.",
|
|
162
|
+
"solar_log_text_efficiency_high": "Das aktuell geschätzte Effizienzverhältnis liegt bei etwa %s, was auf einen sehr guten Solareffekt hinweist.",
|
|
163
|
+
"solar_log_text_efficiency_medium": "Das aktuell geschätzte Effizienzverhältnis liegt bei etwa %s, was auf einen nutzbaren Solareffekt hinweist.",
|
|
164
|
+
"solar_log_text_efficiency_low": "Das aktuell geschätzte Effizienzverhältnis liegt bei etwa %s, was nur auf einen begrenzten Solareffekt hinweist.",
|
|
165
|
+
"solar_log_text_active_minutes": "Solar war heute etwa %s Minuten effektiv aktiv.",
|
|
166
|
+
"solar_log_text_outside_temp": "Die aktuell verwendete Außentemperatur für die Bewertung liegt bei etwa %s °C.",
|
|
167
|
+
"solar_log_text_weather_supported_with_text": "Wetterbasierte Plausibilisierung ist aktiv. Aktuelle Wetterzusammenfassung: %s",
|
|
168
|
+
"solar_log_text_weather_supported": "Für diese Bewertung ist eine wetterbasierte Plausibilisierung aktiv.",
|
|
169
|
+
"solar_log_text_small_gain_detail": "Der aktuell geschätzte Tagesertrag liegt bei etwa %s Wh, das entspricht etwa %s kWh.",
|
|
170
|
+
"solar_log_text_quality_level": "Die aktuelle Qualitätsstufe der Bewertung ist %s."
|
|
105
171
|
}
|