@mxtommy/kip 4.8.0-beta.1 → 4.8.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.
- package/CHANGELOG.md +32 -0
- package/LICENSE +3 -0
- package/README.md +56 -49
- package/package.json +24 -21
- package/plugin/history-series.service.js +108 -30
- package/plugin/index.js +157 -115
- package/plugin/kip-series-contract.js +12 -3
- package/plugin/openApi.json +64 -13
- package/plugin/sqlite-history-storage.service.js +4 -2
- package/plugin/src/history-series.service.js +634 -0
- package/plugin/src/index.js +1322 -0
- package/plugin/src/kip-series-contract.js +31 -0
- package/plugin/src/openApi.json +855 -0
- package/plugin/src/sqlite-history-storage.service.js +1122 -0
- package/public/3rdpartylicenses.txt +569 -727
- package/public/assets/help-docs/chartplotter.md +1 -1
- package/public/assets/help-docs/contributing-widgets.md +40 -0
- package/public/assets/help-docs/dashboards.md +6 -0
- package/public/assets/help-docs/history-api.md +69 -71
- package/public/assets/help-docs/menu.json +27 -7
- package/public/assets/help-docs/time-series.md +81 -0
- package/public/assets/help-docs/welcome.md +1 -1
- package/public/assets/svg/icons.svg +36 -1
- package/public/chunk-00hwknVI.js +1 -0
- package/public/chunk-5U8d2Ygo.js +14 -0
- package/public/chunk-AANk-NXH.js +1015 -0
- package/public/chunk-B6bEhzNR.js +1 -0
- package/public/chunk-B82ADJSs.js +1 -0
- package/public/chunk-BJz7Ij9E.js +1 -0
- package/public/chunk-BKj60jdq.js +1 -0
- package/public/chunk-BOAD0N2a.js +2 -0
- package/public/chunk-BQr-tSkv.js +1 -0
- package/public/chunk-BX6skw5k.js +1 -0
- package/public/chunk-BdokyhBM.js +1 -0
- package/public/chunk-Blokg6Ds.js +1 -0
- package/public/chunk-BoNDCNGE.js +1 -0
- package/public/chunk-Bp0MQwPM.js +1 -0
- package/public/chunk-BwOIef42.js +1 -0
- package/public/chunk-By8ZVvDx.js +2 -0
- package/public/chunk-C2VQxCXa.js +1 -0
- package/public/chunk-C6UXF7BI.js +1 -0
- package/public/chunk-CA6RRaxd.js +1 -0
- package/public/chunk-CIlfzV9R.js +1 -0
- package/public/chunk-CL2xiH57.js +1 -0
- package/public/chunk-CNMFlw_H.js +1 -0
- package/public/chunk-CNNTuNlM.js +1 -0
- package/public/chunk-CQNVyz8q.js +1 -0
- package/public/chunk-CXvG8mMq.js +1 -0
- package/public/chunk-CaD3zVfK.js +1 -0
- package/public/chunk-CkXTl3su.js +1 -0
- package/public/chunk-CpMCBDvH.js +1 -0
- package/public/chunk-CpuB_ZZz.js +2 -0
- package/public/chunk-CrBxxZCe.js +1 -0
- package/public/chunk-CwSa_BNV.js +1 -0
- package/public/chunk-D4xZ2tGB.js +39 -0
- package/public/chunk-DDqeRiB0.js +1 -0
- package/public/chunk-DE4ylhpq.js +1 -0
- package/public/chunk-DNS-qsFC.js +1 -0
- package/public/chunk-DQnoPLyC.js +1 -0
- package/public/chunk-DWLISAZi.js +1 -0
- package/public/chunk-DXkKj21H.js +1 -0
- package/public/chunk-DchvLV_Y.js +1 -0
- package/public/chunk-DjZgzSiH.js +1 -0
- package/public/chunk-DumLX5k_.js +1 -0
- package/public/chunk-Dxlia2NE.js +1 -0
- package/public/chunk-DzTTqoF_.js +1 -0
- package/public/chunk-H9_eqTYx.js +1 -0
- package/public/chunk-Ii9n-oiL.js +1 -0
- package/public/chunk-JbEkcxJm.js +2 -0
- package/public/chunk-MjWuPUhu.js +1 -0
- package/public/chunk-PheXEqie.js +1 -0
- package/public/chunk-TakPRI3t.js +2 -0
- package/public/chunk-VgxMqRPk.js +3 -0
- package/public/chunk-dgaOX1wC.js +382 -0
- package/public/chunk-hd2_AKwt.js +1 -0
- package/public/chunk-nu4vZg-z.js +1 -0
- package/public/chunk-sNakMDHi.js +1 -0
- package/public/chunk-tAiV3ARh.js +1 -0
- package/public/chunk-y1imvaA-.js +1 -0
- package/public/index.html +4 -4
- package/public/main-BAV5VDOD.js +7109 -0
- package/public/polyfills-5MJXXCNC.js +1 -0
- package/public/styles-JZSMCSRO.css +1 -0
- package/public/assets/help-docs/widget-historical-series.md +0 -110
- package/public/chunk-2TP7C66X.js +0 -5
- package/public/chunk-4KHUCTYN.js +0 -3
- package/public/chunk-6ISGIPNP.js +0 -1
- package/public/chunk-7JGTAL26.js +0 -10
- package/public/chunk-C23GLDFH.js +0 -62
- package/public/chunk-CB4E7PPA.js +0 -4
- package/public/chunk-DNM5XUIF.js +0 -1
- package/public/chunk-HF7V6XFA.js +0 -2
- package/public/chunk-HXR2BKDL.js +0 -4
- package/public/chunk-ILQQCGMJ.js +0 -5
- package/public/chunk-IXUKD73N.js +0 -5
- package/public/chunk-JMZYPL54.js +0 -2
- package/public/chunk-KJCBQE6W.js +0 -6
- package/public/chunk-LEY6MANN.js +0 -16
- package/public/chunk-O5BTKN5D.js +0 -1
- package/public/chunk-OH5KNIQ7.js +0 -50
- package/public/chunk-Q32FSCUX.js +0 -1
- package/public/chunk-RIX4VQJ2.js +0 -1
- package/public/chunk-SHJMXSDM.js +0 -1
- package/public/chunk-T4VTC6GW.js +0 -1
- package/public/chunk-VFJD3XKT.js +0 -1
- package/public/chunk-VFZSH4TC.js +0 -1
- package/public/chunk-W6MCE3GH.js +0 -6
- package/public/chunk-WAOG456B.js +0 -2
- package/public/chunk-XBOM6DMF.js +0 -9
- package/public/main-BECMST5R.js +0 -1
- package/public/polyfills-BWA36QKG.js +0 -1
- package/public/scripts-M6PHZBML.js +0 -14
- package/public/styles-DYEO23GZ.css +0 -1
package/plugin/index.js
CHANGED
|
@@ -65,7 +65,7 @@ const start = (server) => {
|
|
|
65
65
|
const CONFIG_SCHEMA = {
|
|
66
66
|
type: 'object',
|
|
67
67
|
title: 'Remote Control and Data Series',
|
|
68
|
-
description: 'NOTE: All plugin settings are also managed from within KIP\'s Display Options panel. Changes made here will be overridden when KIP applies settings from the Display Options.',
|
|
68
|
+
description: 'NOTE: All plugin settings are also managed from within KIP\'s Display Options panel. Changes made here will be overridden when KIP applies settings from the Display Options. See the KIP Time-Series help documentation for details.',
|
|
69
69
|
properties: {
|
|
70
70
|
nodeSqliteAvailable: {
|
|
71
71
|
type: 'boolean',
|
|
@@ -155,37 +155,105 @@ const start = (server) => {
|
|
|
155
155
|
.replace(/[^a-z0-9]+/g, '-')
|
|
156
156
|
.replace(/^-+|-+$/g, '');
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
158
|
+
const ELECTRICAL_SERIES_PREFIX_BY_FAMILY = {
|
|
159
|
+
batteries: 'bms',
|
|
160
|
+
solar: 'solar',
|
|
161
|
+
chargers: 'charger',
|
|
162
|
+
inverters: 'inverter',
|
|
163
|
+
alternators: 'alternator',
|
|
164
|
+
ac: 'ac'
|
|
165
|
+
};
|
|
166
|
+
const ELECTRICAL_SELF_ROOT_BY_FAMILY = {
|
|
167
|
+
batteries: 'electrical.batteries',
|
|
168
|
+
solar: 'electrical.solar',
|
|
169
|
+
chargers: 'electrical.chargers',
|
|
170
|
+
inverters: 'electrical.inverters',
|
|
171
|
+
alternators: 'electrical.alternators',
|
|
172
|
+
ac: 'electrical.ac'
|
|
173
|
+
};
|
|
174
|
+
const ELECTRICAL_METRICS_BY_FAMILY = {
|
|
175
|
+
batteries: ['capacity.stateOfCharge', 'current'],
|
|
176
|
+
solar: ['current', 'panelPower'],
|
|
177
|
+
chargers: ['voltage', 'current'],
|
|
178
|
+
inverters: ['voltage', 'current'],
|
|
179
|
+
alternators: ['voltage', 'current'],
|
|
180
|
+
ac: [
|
|
181
|
+
'line1.voltage',
|
|
182
|
+
'line1.current',
|
|
183
|
+
'line1.frequency',
|
|
184
|
+
'line2.voltage',
|
|
185
|
+
'line2.current',
|
|
186
|
+
'line2.frequency',
|
|
187
|
+
'line3.voltage',
|
|
188
|
+
'line3.current',
|
|
189
|
+
'line3.frequency'
|
|
190
|
+
]
|
|
191
|
+
};
|
|
192
|
+
function resolveFamilyKeyFromTemplateSeries(series) {
|
|
193
|
+
if (!(0, history_series_service_1.isKipElectricalTemplateSeriesDefinition)(series)) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
if (series.familyKey) {
|
|
197
|
+
return series.familyKey;
|
|
198
|
+
}
|
|
199
|
+
switch (series.expansionMode) {
|
|
200
|
+
case 'bms-battery-tree':
|
|
201
|
+
return 'batteries';
|
|
202
|
+
case 'solar-tree':
|
|
203
|
+
return 'solar';
|
|
204
|
+
case 'charger-tree':
|
|
205
|
+
return 'chargers';
|
|
206
|
+
case 'inverter-tree':
|
|
207
|
+
return 'inverters';
|
|
208
|
+
case 'alternator-tree':
|
|
209
|
+
return 'alternators';
|
|
210
|
+
case 'ac-tree':
|
|
211
|
+
return 'ac';
|
|
212
|
+
default:
|
|
170
213
|
return null;
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
const
|
|
175
|
-
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function normalizeAllowedIds(series) {
|
|
217
|
+
const rawAllowedIds = Array.isArray(series.allowedIds)
|
|
218
|
+
? series.allowedIds
|
|
219
|
+
: null;
|
|
220
|
+
if (!Array.isArray(rawAllowedIds)) {
|
|
176
221
|
return [];
|
|
177
222
|
}
|
|
178
|
-
return
|
|
179
|
-
.filter(id =>
|
|
180
|
-
.
|
|
223
|
+
return rawAllowedIds
|
|
224
|
+
.filter((id) => typeof id === 'string')
|
|
225
|
+
.map(id => id.trim())
|
|
226
|
+
.filter(id => id.length > 0);
|
|
227
|
+
}
|
|
228
|
+
function normalizeTrackedDevices(series) {
|
|
229
|
+
if (!Array.isArray(series.trackedDevices)) {
|
|
230
|
+
return [];
|
|
231
|
+
}
|
|
232
|
+
const trackedByKey = new Map();
|
|
233
|
+
series.trackedDevices.forEach(item => {
|
|
234
|
+
if (!item || typeof item !== 'object') {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
const id = typeof item.id === 'string' ? item.id.trim() : '';
|
|
238
|
+
const normalizedSource = typeof item.source === 'string' ? item.source.trim() : '';
|
|
239
|
+
const source = normalizedSource.length > 0 ? normalizedSource : 'default';
|
|
240
|
+
if (!id) {
|
|
241
|
+
return;
|
|
242
|
+
}
|
|
243
|
+
trackedByKey.set(`${id}||${source}`, { id, source });
|
|
244
|
+
});
|
|
245
|
+
return Array.from(trackedByKey.values()).sort((left, right) => {
|
|
246
|
+
const idCompare = left.id.localeCompare(right.id);
|
|
247
|
+
return idCompare !== 0 ? idCompare : left.source.localeCompare(right.source);
|
|
248
|
+
});
|
|
181
249
|
}
|
|
182
|
-
function
|
|
183
|
-
const
|
|
184
|
-
const readCandidate = (
|
|
185
|
-
if (!
|
|
250
|
+
function resolveElectricalIdsFromSelfPath(rootPath) {
|
|
251
|
+
const node = server.getSelfPath(rootPath);
|
|
252
|
+
const readCandidate = (entry) => {
|
|
253
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
186
254
|
return null;
|
|
187
255
|
}
|
|
188
|
-
const root =
|
|
256
|
+
const root = entry;
|
|
189
257
|
if (Object.prototype.hasOwnProperty.call(root, 'value')) {
|
|
190
258
|
const value = root.value;
|
|
191
259
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
@@ -195,7 +263,7 @@ const start = (server) => {
|
|
|
195
263
|
}
|
|
196
264
|
return root;
|
|
197
265
|
};
|
|
198
|
-
const candidates = readCandidate(
|
|
266
|
+
const candidates = readCandidate(node);
|
|
199
267
|
if (!candidates) {
|
|
200
268
|
return [];
|
|
201
269
|
}
|
|
@@ -203,19 +271,13 @@ const start = (server) => {
|
|
|
203
271
|
.filter(id => /^[a-z0-9_-]+$/i.test(id))
|
|
204
272
|
.sort((left, right) => left.localeCompare(right));
|
|
205
273
|
}
|
|
206
|
-
function
|
|
274
|
+
function getExistingConcreteElectricalSeries(templateSeries, familyKey, existingSeries) {
|
|
275
|
+
const rootPrefix = `${ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]}.`;
|
|
207
276
|
return existingSeries
|
|
208
277
|
.filter(series => series.ownerWidgetUuid === templateSeries.ownerWidgetUuid)
|
|
209
278
|
.filter(history_series_service_1.isKipConcreteSeriesDefinition)
|
|
210
279
|
.filter(series => series.seriesId !== templateSeries.seriesId)
|
|
211
|
-
.
|
|
212
|
-
}
|
|
213
|
-
function getExistingConcreteSolarSeries(templateSeries, existingSeries) {
|
|
214
|
-
return existingSeries
|
|
215
|
-
.filter(series => series.ownerWidgetUuid === templateSeries.ownerWidgetUuid)
|
|
216
|
-
.filter(history_series_service_1.isKipConcreteSeriesDefinition)
|
|
217
|
-
.filter(series => series.seriesId !== templateSeries.seriesId)
|
|
218
|
-
.filter(series => series.path.startsWith('electrical.solar.'))
|
|
280
|
+
.filter(series => series.path.startsWith(rootPrefix))
|
|
219
281
|
.map(series => ({ ...series }));
|
|
220
282
|
}
|
|
221
283
|
function mergeSeriesDefinitions(series) {
|
|
@@ -226,91 +288,69 @@ const start = (server) => {
|
|
|
226
288
|
return Array.from(mergedById.values());
|
|
227
289
|
}
|
|
228
290
|
function expandTemplateSeriesDefinitions(payload, existingSeries = []) {
|
|
229
|
-
const bmsMetrics = ['capacity.stateOfCharge', 'current'];
|
|
230
|
-
const solarMetrics = ['current', 'panelPower'];
|
|
231
291
|
const expandedById = new Map();
|
|
232
|
-
const
|
|
233
|
-
const
|
|
292
|
+
const discoveredIdsByFamily = new Map();
|
|
293
|
+
const ensureDiscoveredIds = (familyKey) => {
|
|
294
|
+
if (!discoveredIdsByFamily.has(familyKey)) {
|
|
295
|
+
const discovered = resolveElectricalIdsFromSelfPath(ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]);
|
|
296
|
+
discoveredIdsByFamily.set(familyKey, discovered);
|
|
297
|
+
}
|
|
298
|
+
return discoveredIdsByFamily.get(familyKey) ?? [];
|
|
299
|
+
};
|
|
234
300
|
payload.forEach(series => {
|
|
235
301
|
if (!(0, history_series_service_1.isKipTemplateSeriesDefinition)(series)) {
|
|
236
302
|
expandedById.set(series.seriesId, series);
|
|
237
303
|
return;
|
|
238
304
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
expandedById.set(existing.seriesId, existing);
|
|
243
|
-
});
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
const allowedBatteryIds = Array.isArray(series.allowedBatteryIds)
|
|
247
|
-
? series.allowedBatteryIds
|
|
248
|
-
.filter((id) => typeof id === 'string')
|
|
249
|
-
.map(id => id.trim())
|
|
250
|
-
.filter(id => id.length > 0)
|
|
251
|
-
: [];
|
|
252
|
-
const allowedSet = allowedBatteryIds.length > 0 ? new Set(allowedBatteryIds) : null;
|
|
253
|
-
const batteryIds = discoveredBatteryIds.filter(id => !allowedSet || allowedSet.has(id));
|
|
254
|
-
if (batteryIds.length === 0) {
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
const source = series.source ?? 'default';
|
|
258
|
-
const sourceKey = slugify(source || 'default') || 'default';
|
|
259
|
-
batteryIds.forEach(batteryId => {
|
|
260
|
-
bmsMetrics.forEach(metric => {
|
|
261
|
-
const path = `self.electrical.batteries.${batteryId}.${metric}`;
|
|
262
|
-
const seriesId = `${series.ownerWidgetUuid}:bms:${batteryId}:${metric}:${sourceKey}`;
|
|
263
|
-
expandedById.set(seriesId, {
|
|
264
|
-
...series,
|
|
265
|
-
seriesId,
|
|
266
|
-
datasetUuid: `${series.ownerWidgetUuid}:bms:${batteryId}:${metric}:${sourceKey}`,
|
|
267
|
-
path,
|
|
268
|
-
retentionDurationMs: Number.isFinite(series.retentionDurationMs) ? series.retentionDurationMs : 24 * 60 * 60 * 1000,
|
|
269
|
-
expansionMode: null,
|
|
270
|
-
allowedBatteryIds: null,
|
|
271
|
-
allowedSolarIds: null
|
|
272
|
-
});
|
|
273
|
-
});
|
|
274
|
-
});
|
|
305
|
+
const familyKey = resolveFamilyKeyFromTemplateSeries(series);
|
|
306
|
+
if (!familyKey) {
|
|
307
|
+
expandedById.set(series.seriesId, series);
|
|
275
308
|
return;
|
|
276
309
|
}
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
310
|
+
const seriesPrefix = ELECTRICAL_SERIES_PREFIX_BY_FAMILY[familyKey];
|
|
311
|
+
const metricList = ELECTRICAL_METRICS_BY_FAMILY[familyKey];
|
|
312
|
+
const selfRoot = `self.${ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]}`;
|
|
313
|
+
const trackedDevices = normalizeTrackedDevices(series);
|
|
314
|
+
let concreteDevices = trackedDevices;
|
|
315
|
+
if (concreteDevices.length === 0) {
|
|
316
|
+
const discoveredIds = ensureDiscoveredIds(familyKey);
|
|
317
|
+
if (discoveredIds.length === 0) {
|
|
318
|
+
getExistingConcreteElectricalSeries(series, familyKey, existingSeries).forEach(existing => {
|
|
280
319
|
expandedById.set(existing.seriesId, existing);
|
|
281
320
|
});
|
|
282
321
|
return;
|
|
283
322
|
}
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
.filter(id => id.length > 0)
|
|
289
|
-
: [];
|
|
290
|
-
const allowedSet = allowedSolarIds.length > 0 ? new Set(allowedSolarIds) : null;
|
|
291
|
-
const chargerIds = discoveredSolarIds.filter(id => !allowedSet || allowedSet.has(id));
|
|
292
|
-
if (chargerIds.length === 0) {
|
|
323
|
+
const allowedIds = normalizeAllowedIds(series);
|
|
324
|
+
const allowedSet = allowedIds.length > 0 ? new Set(allowedIds) : null;
|
|
325
|
+
const filteredIds = discoveredIds.filter(id => !allowedSet || allowedSet.has(id));
|
|
326
|
+
if (filteredIds.length === 0) {
|
|
293
327
|
return;
|
|
294
328
|
}
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
329
|
+
const fallbackSource = series.source ?? 'default';
|
|
330
|
+
concreteDevices = filteredIds.map(deviceId => ({
|
|
331
|
+
id: deviceId,
|
|
332
|
+
source: fallbackSource
|
|
333
|
+
}));
|
|
334
|
+
}
|
|
335
|
+
concreteDevices.forEach(device => {
|
|
336
|
+
const sourceKey = slugify(device.source || 'default') || 'default';
|
|
337
|
+
metricList.forEach(metric => {
|
|
338
|
+
const path = `${selfRoot}.${device.id}.${metric}`;
|
|
339
|
+
const seriesId = `${series.ownerWidgetUuid}:${seriesPrefix}:${device.id}:${metric}:${sourceKey}`;
|
|
340
|
+
expandedById.set(seriesId, {
|
|
341
|
+
...series,
|
|
342
|
+
seriesId,
|
|
343
|
+
datasetUuid: `${series.ownerWidgetUuid}:${seriesPrefix}:${device.id}:${metric}:${sourceKey}`,
|
|
344
|
+
path,
|
|
345
|
+
source: device.source,
|
|
346
|
+
retentionDurationMs: Number.isFinite(series.retentionDurationMs) ? series.retentionDurationMs : 24 * 60 * 60 * 1000,
|
|
347
|
+
expansionMode: null,
|
|
348
|
+
familyKey: null,
|
|
349
|
+
allowedIds: null,
|
|
350
|
+
trackedDevices: null
|
|
311
351
|
});
|
|
312
352
|
});
|
|
313
|
-
}
|
|
353
|
+
});
|
|
314
354
|
});
|
|
315
355
|
return Array.from(expandedById.values());
|
|
316
356
|
}
|
|
@@ -358,16 +398,15 @@ const start = (server) => {
|
|
|
358
398
|
}
|
|
359
399
|
}
|
|
360
400
|
function getRouteError(error, fallbackMessage) {
|
|
361
|
-
const message =
|
|
401
|
+
const message = error?.message?.trim() || fallbackMessage;
|
|
362
402
|
const normalized = message.toLowerCase();
|
|
363
403
|
if (normalized.includes('invalid ')
|
|
364
404
|
|| normalized.includes('missing ')
|
|
365
|
-
|| normalized.includes('
|
|
366
|
-
|| normalized.includes('required')
|
|
367
|
-
|| normalized.includes('expected an iso')) {
|
|
405
|
+
|| normalized.includes('required')) {
|
|
368
406
|
return { statusCode: 400, message };
|
|
369
407
|
}
|
|
370
408
|
if (normalized.includes('sqlite')
|
|
409
|
+
|| normalized.includes('database')
|
|
371
410
|
|| normalized.includes('storage unavailable')
|
|
372
411
|
|| normalized.includes('not initialized')
|
|
373
412
|
|| isSqliteUnavailable()) {
|
|
@@ -1228,16 +1267,19 @@ const start = (server) => {
|
|
|
1228
1267
|
const scopedPayload = payload.map(series => ({
|
|
1229
1268
|
...series
|
|
1230
1269
|
}));
|
|
1231
|
-
const isBatteryDiscoveryUnavailable = resolveBmsBatteryIdsFromSelfPath().length === 0;
|
|
1232
|
-
const isSolarDiscoveryUnavailable = resolveSolarIdsFromSelfPath().length === 0;
|
|
1233
1270
|
const preservedTemplateConcreteSeries = scopedPayload
|
|
1234
1271
|
.filter(history_series_service_1.isKipTemplateSeriesDefinition)
|
|
1235
1272
|
.flatMap(series => {
|
|
1236
|
-
|
|
1237
|
-
|
|
1273
|
+
const familyKey = resolveFamilyKeyFromTemplateSeries(series);
|
|
1274
|
+
if (!familyKey) {
|
|
1275
|
+
return [];
|
|
1276
|
+
}
|
|
1277
|
+
if (normalizeTrackedDevices(series).length > 0) {
|
|
1278
|
+
return [];
|
|
1238
1279
|
}
|
|
1239
|
-
|
|
1240
|
-
|
|
1280
|
+
const isDiscoveryUnavailable = resolveElectricalIdsFromSelfPath(ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]).length === 0;
|
|
1281
|
+
if (isDiscoveryUnavailable) {
|
|
1282
|
+
return getExistingConcreteElectricalSeries(series, familyKey, currentSeries);
|
|
1241
1283
|
}
|
|
1242
1284
|
return [];
|
|
1243
1285
|
});
|
|
@@ -1,18 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isKipTemplateSeriesDefinition = isKipTemplateSeriesDefinition;
|
|
4
|
+
exports.isKipElectricalTemplateSeriesDefinition = isKipElectricalTemplateSeriesDefinition;
|
|
4
5
|
exports.isKipBmsTemplateSeriesDefinition = isKipBmsTemplateSeriesDefinition;
|
|
5
6
|
exports.isKipSolarTemplateSeriesDefinition = isKipSolarTemplateSeriesDefinition;
|
|
6
7
|
exports.isKipConcreteSeriesDefinition = isKipConcreteSeriesDefinition;
|
|
7
8
|
exports.isKipSeriesEnabled = isKipSeriesEnabled;
|
|
8
9
|
function isKipTemplateSeriesDefinition(series) {
|
|
9
|
-
return series.expansionMode === 'bms-battery-tree'
|
|
10
|
+
return series.expansionMode === 'bms-battery-tree'
|
|
11
|
+
|| series.expansionMode === 'solar-tree'
|
|
12
|
+
|| series.expansionMode === 'charger-tree'
|
|
13
|
+
|| series.expansionMode === 'inverter-tree'
|
|
14
|
+
|| series.expansionMode === 'alternator-tree'
|
|
15
|
+
|| series.expansionMode === 'ac-tree';
|
|
16
|
+
}
|
|
17
|
+
function isKipElectricalTemplateSeriesDefinition(series) {
|
|
18
|
+
return isKipTemplateSeriesDefinition(series);
|
|
10
19
|
}
|
|
11
20
|
function isKipBmsTemplateSeriesDefinition(series) {
|
|
12
|
-
return series.expansionMode === 'bms-battery-tree';
|
|
21
|
+
return series.expansionMode === 'bms-battery-tree' && (series.familyKey == null || series.familyKey === 'batteries');
|
|
13
22
|
}
|
|
14
23
|
function isKipSolarTemplateSeriesDefinition(series) {
|
|
15
|
-
return series.expansionMode === 'solar-tree';
|
|
24
|
+
return series.expansionMode === 'solar-tree' && (series.familyKey == null || series.familyKey === 'solar');
|
|
16
25
|
}
|
|
17
26
|
function isKipConcreteSeriesDefinition(series) {
|
|
18
27
|
return series.expansionMode == null;
|
package/plugin/openApi.json
CHANGED
|
@@ -217,21 +217,38 @@
|
|
|
217
217
|
"nullable": true,
|
|
218
218
|
"description": "Null for concrete series definitions."
|
|
219
219
|
},
|
|
220
|
-
"
|
|
220
|
+
"familyKey": {
|
|
221
|
+
"type": "string",
|
|
222
|
+
"nullable": true,
|
|
223
|
+
"description": "Concrete series do not use family key and should leave this null."
|
|
224
|
+
},
|
|
225
|
+
"allowedIds": {
|
|
221
226
|
"type": "array",
|
|
222
227
|
"nullable": true,
|
|
223
228
|
"items": {
|
|
224
229
|
"type": "string"
|
|
225
230
|
},
|
|
226
|
-
"description": "Concrete series do not use
|
|
231
|
+
"description": "Concrete series do not use template filters and should leave this null."
|
|
227
232
|
},
|
|
228
|
-
"
|
|
233
|
+
"trackedDevices": {
|
|
229
234
|
"type": "array",
|
|
230
235
|
"nullable": true,
|
|
231
236
|
"items": {
|
|
232
|
-
"type": "
|
|
237
|
+
"type": "object",
|
|
238
|
+
"properties": {
|
|
239
|
+
"id": {
|
|
240
|
+
"type": "string"
|
|
241
|
+
},
|
|
242
|
+
"source": {
|
|
243
|
+
"type": "string"
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
"required": [
|
|
247
|
+
"id",
|
|
248
|
+
"source"
|
|
249
|
+
]
|
|
233
250
|
},
|
|
234
|
-
"description": "Concrete series do not use
|
|
251
|
+
"description": "Concrete series do not use tracked device template filters and should leave this null."
|
|
235
252
|
}
|
|
236
253
|
}
|
|
237
254
|
}
|
|
@@ -249,34 +266,68 @@
|
|
|
249
266
|
"type": "string",
|
|
250
267
|
"enum": [
|
|
251
268
|
"widget-bms",
|
|
252
|
-
"widget-solar-charger"
|
|
269
|
+
"widget-solar-charger",
|
|
270
|
+
"widget-charger",
|
|
271
|
+
"widget-inverter",
|
|
272
|
+
"widget-alternator",
|
|
273
|
+
"widget-ac"
|
|
253
274
|
]
|
|
254
275
|
},
|
|
255
276
|
"expansionMode": {
|
|
256
277
|
"type": "string",
|
|
257
278
|
"enum": [
|
|
258
279
|
"bms-battery-tree",
|
|
259
|
-
"solar-tree"
|
|
280
|
+
"solar-tree",
|
|
281
|
+
"charger-tree",
|
|
282
|
+
"inverter-tree",
|
|
283
|
+
"alternator-tree",
|
|
284
|
+
"ac-tree"
|
|
260
285
|
]
|
|
261
286
|
},
|
|
262
|
-
"
|
|
287
|
+
"familyKey": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"enum": [
|
|
290
|
+
"batteries",
|
|
291
|
+
"solar",
|
|
292
|
+
"chargers",
|
|
293
|
+
"inverters",
|
|
294
|
+
"alternators",
|
|
295
|
+
"ac"
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
"allowedIds": {
|
|
263
299
|
"type": "array",
|
|
264
300
|
"nullable": true,
|
|
265
301
|
"items": {
|
|
266
302
|
"type": "string"
|
|
267
|
-
}
|
|
303
|
+
},
|
|
304
|
+
"description": "Template device IDs to include. Null or empty includes all discovered devices."
|
|
268
305
|
},
|
|
269
|
-
"
|
|
306
|
+
"trackedDevices": {
|
|
270
307
|
"type": "array",
|
|
271
308
|
"nullable": true,
|
|
272
309
|
"items": {
|
|
273
|
-
"type": "
|
|
274
|
-
|
|
310
|
+
"type": "object",
|
|
311
|
+
"properties": {
|
|
312
|
+
"id": {
|
|
313
|
+
"type": "string"
|
|
314
|
+
},
|
|
315
|
+
"source": {
|
|
316
|
+
"type": "string"
|
|
317
|
+
}
|
|
318
|
+
},
|
|
319
|
+
"required": [
|
|
320
|
+
"id",
|
|
321
|
+
"source"
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
"description": "Explicit tracked device id/source pairs to expand. When provided, template expansion skips self-tree discovery and emits one concrete series per pair and metric."
|
|
275
325
|
}
|
|
276
326
|
},
|
|
277
327
|
"required": [
|
|
278
328
|
"ownerWidgetSelector",
|
|
279
|
-
"expansionMode"
|
|
329
|
+
"expansionMode",
|
|
330
|
+
"familyKey"
|
|
280
331
|
]
|
|
281
332
|
}
|
|
282
333
|
]
|
|
@@ -709,7 +709,8 @@ class SqliteHistoryStorageService {
|
|
|
709
709
|
});
|
|
710
710
|
}
|
|
711
711
|
catch (err) {
|
|
712
|
-
|
|
712
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
713
|
+
this.logger.error(`[SERIES STORAGE] Prune failed: ${message}`);
|
|
713
714
|
}
|
|
714
715
|
}, SqliteHistoryStorageService.FOUR_HOURS_INTERVAL);
|
|
715
716
|
this.pruneJob.unref?.();
|
|
@@ -739,7 +740,8 @@ class SqliteHistoryStorageService {
|
|
|
739
740
|
});
|
|
740
741
|
}
|
|
741
742
|
catch (err) {
|
|
742
|
-
|
|
743
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
744
|
+
this.logger.error(`[SERIES STORAGE] Stale series cleanup failed: ${message}`);
|
|
743
745
|
}
|
|
744
746
|
}, SqliteHistoryStorageService.EIGHT_HOURS_INTERVAL);
|
|
745
747
|
this.staleSeriesCleanupJob.unref?.();
|