@mxtommy/kip 4.7.0 → 4.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +10 -0
- package/LICENSE +3 -0
- package/README.md +37 -29
- package/package.json +23 -34
- package/plugin/history-series.service.js +108 -11
- package/plugin/index.js +170 -44
- package/plugin/kip-series-contract.js +18 -1
- package/plugin/openApi.json +75 -7
- package/public/3rdpartylicenses.txt +0 -26
- package/public/assets/help-docs/contributing-widgets.md +40 -0
- package/public/assets/help-docs/dashboards.md +8 -0
- package/public/assets/help-docs/menu.json +4 -2
- package/public/assets/svg/icons.svg +48 -1
- package/public/assets/svg/symbols.svg +68 -0
- package/public/{chunk-PKB75FW2.js → chunk-2TP7C66X.js} +1 -1
- package/public/chunk-3SJSLBCV.js +6 -0
- package/public/chunk-5SMHTIVS.js +50 -0
- package/public/{chunk-E3ROX3JL.js → chunk-6ISGIPNP.js} +1 -1
- package/public/chunk-6ZR3M3IQ.js +3 -0
- package/public/{chunk-6LPU67GV.js → chunk-ACY7HYMZ.js} +1 -1
- package/public/{chunk-E3VC75HO.js → chunk-C23GLDFH.js} +1 -1
- package/public/{chunk-W5RYIISE.js → chunk-CB4E7PPA.js} +1 -1
- package/public/{chunk-JL7DODKZ.js → chunk-DNM5XUIF.js} +1 -1
- package/public/{chunk-2BKBAKG4.js → chunk-GSM7FZE2.js} +2 -2
- package/public/{chunk-OBMTAHXF.js → chunk-HF7V6XFA.js} +1 -1
- package/public/{chunk-2BY2XRFX.js → chunk-HUCDJ6CS.js} +7 -7
- package/public/{chunk-CSJ2TU72.js → chunk-ILQQCGMJ.js} +1 -1
- package/public/chunk-IXUKD73N.js +5 -0
- package/public/{chunk-BMEO4VRV.js → chunk-JMZYPL54.js} +1 -1
- package/public/chunk-KG4C6HZC.js +1 -0
- package/public/{chunk-UBBLNNPA.js → chunk-LEY6MANN.js} +13 -13
- package/public/chunk-NS2OV2OW.js +9 -0
- package/public/{chunk-3JK476CX.js → chunk-O5BTKN5D.js} +1 -1
- package/public/{chunk-PDJFHMII.js → chunk-Q32FSCUX.js} +1 -1
- package/public/{chunk-3B5RVPSS.js → chunk-SHJMXSDM.js} +1 -1
- package/public/{chunk-GWAFO3MC.js → chunk-T4VTC6GW.js} +1 -1
- package/public/{chunk-GHPW4BPE.js → chunk-VFJD3XKT.js} +1 -1
- package/public/{chunk-KHZPPOSI.js → chunk-VFZSH4TC.js} +1 -1
- package/public/{chunk-S4UHQERA.js → chunk-W6MCE3GH.js} +1 -1
- package/public/index.html +1 -1
- package/public/main-UBENKC2D.js +1 -0
- package/public/polyfills-BWA36QKG.js +1 -0
- package/public/chunk-B2CYEVGO.js +0 -9
- package/public/chunk-HIXACNNV.js +0 -6
- package/public/chunk-QN4XMV3X.js +0 -50
- package/public/chunk-U5QHLGHD.js +0 -1
- package/public/chunk-WYC65OAX.js +0 -5
- package/public/chunk-XFHXLFTX.js +0 -3
- package/public/main-WGYSOKJC.js +0 -1
- package/public/polyfills-L4FJGPOC.js +0 -2
package/plugin/index.js
CHANGED
|
@@ -155,13 +155,105 @@ const start = (server) => {
|
|
|
155
155
|
.replace(/[^a-z0-9]+/g, '-')
|
|
156
156
|
.replace(/^-+|-+$/g, '');
|
|
157
157
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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:
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function normalizeAllowedIds(series) {
|
|
217
|
+
const rawAllowedIds = Array.isArray(series.allowedIds)
|
|
218
|
+
? series.allowedIds
|
|
219
|
+
: null;
|
|
220
|
+
if (!Array.isArray(rawAllowedIds)) {
|
|
221
|
+
return [];
|
|
222
|
+
}
|
|
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
|
+
});
|
|
249
|
+
}
|
|
250
|
+
function resolveElectricalIdsFromSelfPath(rootPath) {
|
|
251
|
+
const node = server.getSelfPath(rootPath);
|
|
252
|
+
const readCandidate = (entry) => {
|
|
253
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
162
254
|
return null;
|
|
163
255
|
}
|
|
164
|
-
const root =
|
|
256
|
+
const root = entry;
|
|
165
257
|
if (Object.prototype.hasOwnProperty.call(root, 'value')) {
|
|
166
258
|
const value = root.value;
|
|
167
259
|
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
|
@@ -171,7 +263,7 @@ const start = (server) => {
|
|
|
171
263
|
}
|
|
172
264
|
return root;
|
|
173
265
|
};
|
|
174
|
-
const candidates = readCandidate(
|
|
266
|
+
const candidates = readCandidate(node);
|
|
175
267
|
if (!candidates) {
|
|
176
268
|
return [];
|
|
177
269
|
}
|
|
@@ -179,11 +271,13 @@ const start = (server) => {
|
|
|
179
271
|
.filter(id => /^[a-z0-9_-]+$/i.test(id))
|
|
180
272
|
.sort((left, right) => left.localeCompare(right));
|
|
181
273
|
}
|
|
182
|
-
function
|
|
274
|
+
function getExistingConcreteElectricalSeries(templateSeries, familyKey, existingSeries) {
|
|
275
|
+
const rootPrefix = `${ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]}.`;
|
|
183
276
|
return existingSeries
|
|
184
277
|
.filter(series => series.ownerWidgetUuid === templateSeries.ownerWidgetUuid)
|
|
185
278
|
.filter(history_series_service_1.isKipConcreteSeriesDefinition)
|
|
186
279
|
.filter(series => series.seriesId !== templateSeries.seriesId)
|
|
280
|
+
.filter(series => series.path.startsWith(rootPrefix))
|
|
187
281
|
.map(series => ({ ...series }));
|
|
188
282
|
}
|
|
189
283
|
function mergeSeriesDefinitions(series) {
|
|
@@ -194,44 +288,66 @@ const start = (server) => {
|
|
|
194
288
|
return Array.from(mergedById.values());
|
|
195
289
|
}
|
|
196
290
|
function expandTemplateSeriesDefinitions(payload, existingSeries = []) {
|
|
197
|
-
const bmsMetrics = ['capacity.stateOfCharge', 'current'];
|
|
198
291
|
const expandedById = new Map();
|
|
199
|
-
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
|
+
};
|
|
200
300
|
payload.forEach(series => {
|
|
201
301
|
if (!(0, history_series_service_1.isKipTemplateSeriesDefinition)(series)) {
|
|
202
302
|
expandedById.set(series.seriesId, series);
|
|
203
303
|
return;
|
|
204
304
|
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
});
|
|
305
|
+
const familyKey = resolveFamilyKeyFromTemplateSeries(series);
|
|
306
|
+
if (!familyKey) {
|
|
307
|
+
expandedById.set(series.seriesId, series);
|
|
209
308
|
return;
|
|
210
309
|
}
|
|
211
|
-
const
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
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 => {
|
|
319
|
+
expandedById.set(existing.seriesId, existing);
|
|
320
|
+
});
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
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) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
329
|
+
const fallbackSource = series.source ?? 'default';
|
|
330
|
+
concreteDevices = filteredIds.map(deviceId => ({
|
|
331
|
+
id: deviceId,
|
|
332
|
+
source: fallbackSource
|
|
333
|
+
}));
|
|
221
334
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
const
|
|
227
|
-
const seriesId = `${series.ownerWidgetUuid}:bms:${batteryId}:${metric}:${sourceKey}`;
|
|
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}`;
|
|
228
340
|
expandedById.set(seriesId, {
|
|
229
341
|
...series,
|
|
230
342
|
seriesId,
|
|
231
|
-
datasetUuid: `${series.ownerWidgetUuid}
|
|
343
|
+
datasetUuid: `${series.ownerWidgetUuid}:${seriesPrefix}:${device.id}:${metric}:${sourceKey}`,
|
|
232
344
|
path,
|
|
345
|
+
source: device.source,
|
|
233
346
|
retentionDurationMs: Number.isFinite(series.retentionDurationMs) ? series.retentionDurationMs : 24 * 60 * 60 * 1000,
|
|
234
|
-
expansionMode: null
|
|
347
|
+
expansionMode: null,
|
|
348
|
+
familyKey: null,
|
|
349
|
+
allowedIds: null,
|
|
350
|
+
trackedDevices: null
|
|
235
351
|
});
|
|
236
352
|
});
|
|
237
353
|
});
|
|
@@ -282,16 +398,15 @@ const start = (server) => {
|
|
|
282
398
|
}
|
|
283
399
|
}
|
|
284
400
|
function getRouteError(error, fallbackMessage) {
|
|
285
|
-
const message =
|
|
401
|
+
const message = error?.message?.trim() || fallbackMessage;
|
|
286
402
|
const normalized = message.toLowerCase();
|
|
287
403
|
if (normalized.includes('invalid ')
|
|
288
404
|
|| normalized.includes('missing ')
|
|
289
|
-
|| normalized.includes('
|
|
290
|
-
|| normalized.includes('required')
|
|
291
|
-
|| normalized.includes('expected an iso')) {
|
|
405
|
+
|| normalized.includes('required')) {
|
|
292
406
|
return { statusCode: 400, message };
|
|
293
407
|
}
|
|
294
408
|
if (normalized.includes('sqlite')
|
|
409
|
+
|| normalized.includes('database')
|
|
295
410
|
|| normalized.includes('storage unavailable')
|
|
296
411
|
|| normalized.includes('not initialized')
|
|
297
412
|
|| isSqliteUnavailable()) {
|
|
@@ -1152,21 +1267,32 @@ const start = (server) => {
|
|
|
1152
1267
|
const scopedPayload = payload.map(series => ({
|
|
1153
1268
|
...series
|
|
1154
1269
|
}));
|
|
1155
|
-
const
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1270
|
+
const preservedTemplateConcreteSeries = scopedPayload
|
|
1271
|
+
.filter(history_series_service_1.isKipTemplateSeriesDefinition)
|
|
1272
|
+
.flatMap(series => {
|
|
1273
|
+
const familyKey = resolveFamilyKeyFromTemplateSeries(series);
|
|
1274
|
+
if (!familyKey) {
|
|
1275
|
+
return [];
|
|
1276
|
+
}
|
|
1277
|
+
if (normalizeTrackedDevices(series).length > 0) {
|
|
1278
|
+
return [];
|
|
1279
|
+
}
|
|
1280
|
+
const isDiscoveryUnavailable = resolveElectricalIdsFromSelfPath(ELECTRICAL_SELF_ROOT_BY_FAMILY[familyKey]).length === 0;
|
|
1281
|
+
if (isDiscoveryUnavailable) {
|
|
1282
|
+
return getExistingConcreteElectricalSeries(series, familyKey, currentSeries);
|
|
1283
|
+
}
|
|
1284
|
+
return [];
|
|
1285
|
+
});
|
|
1161
1286
|
const expandedPayload = mergeSeriesDefinitions([
|
|
1162
1287
|
...expandTemplateSeriesDefinitions(scopedPayload, currentSeries),
|
|
1163
|
-
...
|
|
1288
|
+
...preservedTemplateConcreteSeries
|
|
1164
1289
|
]);
|
|
1165
1290
|
const result = simulated.reconcileSeries(expandedPayload);
|
|
1166
1291
|
const nextSeries = simulated.listSeries();
|
|
1167
1292
|
await storageService.replaceSeriesDefinitions(nextSeries);
|
|
1168
|
-
|
|
1169
|
-
|
|
1293
|
+
// /series/reconcile expects the full desired set for KIP-managed series.
|
|
1294
|
+
// Keep in-memory state aligned with persisted state to avoid reintroducing stale series.
|
|
1295
|
+
historySeries.reconcileSeries(nextSeries);
|
|
1170
1296
|
server.debug(`[KIP][SERIES_RECONCILE] created=${result.created} updated=${result.updated} deleted=${result.deleted} total=${result.total}`);
|
|
1171
1297
|
rebuildSeriesCaptureSubscriptions();
|
|
1172
1298
|
return sendOk(res, result);
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.isKipTemplateSeriesDefinition = isKipTemplateSeriesDefinition;
|
|
4
|
+
exports.isKipElectricalTemplateSeriesDefinition = isKipElectricalTemplateSeriesDefinition;
|
|
5
|
+
exports.isKipBmsTemplateSeriesDefinition = isKipBmsTemplateSeriesDefinition;
|
|
6
|
+
exports.isKipSolarTemplateSeriesDefinition = isKipSolarTemplateSeriesDefinition;
|
|
4
7
|
exports.isKipConcreteSeriesDefinition = isKipConcreteSeriesDefinition;
|
|
5
8
|
exports.isKipSeriesEnabled = isKipSeriesEnabled;
|
|
6
9
|
function isKipTemplateSeriesDefinition(series) {
|
|
7
|
-
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);
|
|
19
|
+
}
|
|
20
|
+
function isKipBmsTemplateSeriesDefinition(series) {
|
|
21
|
+
return series.expansionMode === 'bms-battery-tree' && (series.familyKey == null || series.familyKey === 'batteries');
|
|
22
|
+
}
|
|
23
|
+
function isKipSolarTemplateSeriesDefinition(series) {
|
|
24
|
+
return series.expansionMode === 'solar-tree' && (series.familyKey == null || series.familyKey === 'solar');
|
|
8
25
|
}
|
|
9
26
|
function isKipConcreteSeriesDefinition(series) {
|
|
10
27
|
return series.expansionMode == null;
|
package/plugin/openApi.json
CHANGED
|
@@ -217,13 +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."
|
|
232
|
+
},
|
|
233
|
+
"trackedDevices": {
|
|
234
|
+
"type": "array",
|
|
235
|
+
"nullable": true,
|
|
236
|
+
"items": {
|
|
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
|
+
]
|
|
250
|
+
},
|
|
251
|
+
"description": "Concrete series do not use tracked device template filters and should leave this null."
|
|
227
252
|
}
|
|
228
253
|
}
|
|
229
254
|
}
|
|
@@ -240,26 +265,69 @@
|
|
|
240
265
|
"ownerWidgetSelector": {
|
|
241
266
|
"type": "string",
|
|
242
267
|
"enum": [
|
|
243
|
-
"widget-bms"
|
|
268
|
+
"widget-bms",
|
|
269
|
+
"widget-solar-charger",
|
|
270
|
+
"widget-charger",
|
|
271
|
+
"widget-inverter",
|
|
272
|
+
"widget-alternator",
|
|
273
|
+
"widget-ac"
|
|
244
274
|
]
|
|
245
275
|
},
|
|
246
276
|
"expansionMode": {
|
|
247
277
|
"type": "string",
|
|
248
278
|
"enum": [
|
|
249
|
-
"bms-battery-tree"
|
|
279
|
+
"bms-battery-tree",
|
|
280
|
+
"solar-tree",
|
|
281
|
+
"charger-tree",
|
|
282
|
+
"inverter-tree",
|
|
283
|
+
"alternator-tree",
|
|
284
|
+
"ac-tree"
|
|
250
285
|
]
|
|
251
286
|
},
|
|
252
|
-
"
|
|
287
|
+
"familyKey": {
|
|
288
|
+
"type": "string",
|
|
289
|
+
"enum": [
|
|
290
|
+
"batteries",
|
|
291
|
+
"solar",
|
|
292
|
+
"chargers",
|
|
293
|
+
"inverters",
|
|
294
|
+
"alternators",
|
|
295
|
+
"ac"
|
|
296
|
+
]
|
|
297
|
+
},
|
|
298
|
+
"allowedIds": {
|
|
253
299
|
"type": "array",
|
|
254
300
|
"nullable": true,
|
|
255
301
|
"items": {
|
|
256
302
|
"type": "string"
|
|
257
|
-
}
|
|
303
|
+
},
|
|
304
|
+
"description": "Template device IDs to include. Null or empty includes all discovered devices."
|
|
305
|
+
},
|
|
306
|
+
"trackedDevices": {
|
|
307
|
+
"type": "array",
|
|
308
|
+
"nullable": true,
|
|
309
|
+
"items": {
|
|
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."
|
|
258
325
|
}
|
|
259
326
|
},
|
|
260
327
|
"required": [
|
|
261
328
|
"ownerWidgetSelector",
|
|
262
|
-
"expansionMode"
|
|
329
|
+
"expansionMode",
|
|
330
|
+
"familyKey"
|
|
263
331
|
]
|
|
264
332
|
}
|
|
265
333
|
]
|
|
@@ -1061,32 +1061,6 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
1061
1061
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1062
1062
|
THE SOFTWARE.
|
|
1063
1063
|
|
|
1064
|
-
--------------------------------------------------------------------------------
|
|
1065
|
-
Package: zone.js
|
|
1066
|
-
License: "MIT"
|
|
1067
|
-
|
|
1068
|
-
The MIT License
|
|
1069
|
-
|
|
1070
|
-
Copyright (c) 2010-2025 Google LLC. https://angular.dev/license
|
|
1071
|
-
|
|
1072
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
1073
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
1074
|
-
in the Software without restriction, including without limitation the rights
|
|
1075
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
1076
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
1077
|
-
furnished to do so, subject to the following conditions:
|
|
1078
|
-
|
|
1079
|
-
The above copyright notice and this permission notice shall be included in
|
|
1080
|
-
all copies or substantial portions of the Software.
|
|
1081
|
-
|
|
1082
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
1083
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
1084
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
1085
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
1086
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
1087
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
1088
|
-
THE SOFTWARE.
|
|
1089
|
-
|
|
1090
1064
|
--------------------------------------------------------------------------------
|
|
1091
1065
|
Package: prismjs
|
|
1092
1066
|
License: "MIT"
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
## Contributing Widgets
|
|
2
|
+
|
|
3
|
+
KIP widgets are community-powered. If you need a widget that does not exist yet, you can request one from the community or build and contribute it.
|
|
4
|
+
|
|
5
|
+
## For End Users
|
|
6
|
+
|
|
7
|
+
- Check [Dashboards and Layout](#/help/dashboards.md) to confirm the current widget catalog.
|
|
8
|
+
- Ask in the community first before building from scratch; someone may already have a branch or reusable approach.
|
|
9
|
+
- Use [The Embed Page Viewer](#/help/embedwidget.md) when your requirement is best served by an external web dashboard.
|
|
10
|
+
|
|
11
|
+
## For Developers
|
|
12
|
+
|
|
13
|
+
Widget creation follows a schematic-first workflow for Host2 widgets.
|
|
14
|
+
|
|
15
|
+
1. Scaffold with `npm run generate:widget`.
|
|
16
|
+
2. Follow schematic flags and examples in `docs/widget-schematic.md`.
|
|
17
|
+
3. Apply Host2 implementation patterns and stream wiring guardrails.
|
|
18
|
+
4. Add domain assertions to the generated spec before submitting a PR.
|
|
19
|
+
|
|
20
|
+
Developer references:
|
|
21
|
+
- `docs/widget-schematic.md`
|
|
22
|
+
- `.github/instructions/project.instructions.md`
|
|
23
|
+
- `.agents/skills/kip-widget-creation/SKILL.md`
|
|
24
|
+
- `.agents/skills/kip-host2-widget/SKILL.md`
|
|
25
|
+
|
|
26
|
+
## Contribution Flow
|
|
27
|
+
|
|
28
|
+
1. Create a feature branch.
|
|
29
|
+
2. Generate and implement the widget.
|
|
30
|
+
3. Generate and implement the widget test when necessary.
|
|
31
|
+
4. Add an svg icon
|
|
32
|
+
5. Update README.md and KIP documentation with your plugin information.
|
|
33
|
+
6. Validate with lint/tests (ng lint/ng test).
|
|
34
|
+
7. Submit a pull request with screenshots and a short behavior summary.
|
|
35
|
+
|
|
36
|
+
## Notes
|
|
37
|
+
|
|
38
|
+
- Keep new widget behavior deterministic and path-safe.
|
|
39
|
+
- Prefer theme roles and shared formatting helpers.
|
|
40
|
+
- Keep docs concise and link to canonical references rather than duplicating option tables.
|
|
@@ -81,6 +81,9 @@ KIP widgets turn Signal K data into readable visuals and controls. Available wid
|
|
|
81
81
|
- **Classic Steel** – Traditional steel-look linear & radial gauges with range sizes and zone highlights.
|
|
82
82
|
- **Windsteer** – Combines wind, wind sectors, heading, COG, and waypoint info for wind steering.
|
|
83
83
|
- **Wind Trends** – Real-time True Wind trends with dual axes for direction and speed, live values, and averages.
|
|
84
|
+
- **Battery Monitor** - Display batteries or whole banks state State of Charge, remaining capacity, remaining time, voltage, current, power flow, and temperature.
|
|
85
|
+
- **Solar Charger**- Track solar generation and charging performance at a glance with live panel output, battery-side metrics, and clear charger and relay status indicators.
|
|
86
|
+
- **AC/DC Charger**- Monitor charging performance at a glance with a compact AC/DC Charger Widget. View single or multiple chargers with charge mode, voltage, current, power and temperature. Chargers are discovered automatically.
|
|
84
87
|
- **Freeboard-SK** – Adds the Freeboard-SK chart plotter as a widget with automatic sign-in.
|
|
85
88
|
- **Autopilot Head** – Typical autopilot controls for compatible Signal K Autopilot devices.
|
|
86
89
|
- **Realtime Data Chart** – Visualizes data on a real-time chart with actuals, averages, and min/max.
|
|
@@ -91,6 +94,11 @@ KIP widgets turn Signal K data into readable visuals and controls. Available wid
|
|
|
91
94
|
- **Racer - Start Timer** – Advanced racing countdown timer with OCS status and auto dashboard switching.
|
|
92
95
|
- **Countdown Timer** – Simple race start countdown timer with start, pause, sync, and reset options.
|
|
93
96
|
|
|
97
|
+
### Need a Widget Not Listed Here?
|
|
98
|
+
|
|
99
|
+
- Check the community first: someone may already have a reusable setup or branch.
|
|
100
|
+
- If you are a developer, see [Contributing Widgets](#/help/contributing-widgets.md) for the contribution path.
|
|
101
|
+
- If you only need external visuals, consider [The Embed Page Viewer](#/help/embedwidget.md) for web dashboards.
|
|
94
102
|
|
|
95
103
|
## Performance & Layout Tips
|
|
96
104
|
- Favor clarity over cramming: leave space around high‑priority values
|
|
@@ -14,13 +14,15 @@
|
|
|
14
14
|
{ "title": "The Embed Page Viewer", "file": "embedwidget.md" },
|
|
15
15
|
{ "title": "External History-API Provider", "file": "history-api.md" },
|
|
16
16
|
{ "title": "Grafana Integration", "file": "grafana.md" },
|
|
17
|
-
{ "title": "InfluxDB and Signal K", "file": "influxdb.md" }
|
|
17
|
+
{ "title": "InfluxDB and Signal K", "file": "influxdb.md" },
|
|
18
|
+
{ "title": "Contributing Widgets", "file": "contributing-widgets.md" }
|
|
18
19
|
]
|
|
19
20
|
},
|
|
20
21
|
{
|
|
21
22
|
"title": "Advanced Features",
|
|
22
23
|
"items": [
|
|
23
|
-
{ "title": "Kiosk Mode", "file": "kiosk.md" }
|
|
24
|
+
{ "title": "Kiosk Mode", "file": "kiosk.md" },
|
|
25
|
+
{ "title": "Contributing Widgets", "file": "contributing-widgets.md" }
|
|
24
26
|
]
|
|
25
27
|
},
|
|
26
28
|
{ "title": "Online Community Content", "file": "community.md" },
|
|
@@ -337,7 +337,7 @@
|
|
|
337
337
|
<path stroke="currentColor" stroke-linecap="round" stroke-miterlimit="10" stroke-width="3" d="M44.999 20.501v6.9987"></path>
|
|
338
338
|
</svg>
|
|
339
339
|
<svg id="battery_charging" xmlns="http://www.w3.org/2000/svg" fill="currentColor" viewBox="0 0 16 16">
|
|
340
|
-
<path fill="var(--mat-sys-primary)" d="M9.585 2.568a0.5 0.5 0 0 1 0.226 0.58L8.677 6.832h1.99a0.5 0.5 0 0 1 0.364 0.843l-5.334 5.667a0.5 0.5 0 0 1 -0.842 -0.49L5.99 9.167H4a0.5 0.5 0 0 1 -0.364 -0.843l5.333 -5.667a0.5 0.5 0 0 1 0.616 -0.09z"
|
|
340
|
+
<path fill="var(--mat-sys-primary)" stroke-width="1" d="M9.585 2.568a0.5 0.5 0 0 1 0.226 0.58L8.677 6.832h1.99a0.5 0.5 0 0 1 0.364 0.843l-5.334 5.667a0.5 0.5 0 0 1 -0.842 -0.49L5.99 9.167H4a0.5 0.5 0 0 1 -0.364 -0.843l5.333 -5.667a0.5 0.5 0 0 1 0.616 -0.09z" />
|
|
341
341
|
<path d="M2 4h4.332l-0.94 1H2a1 1 0 0 0 -1 1v4a1 1 0 0 0 1 1h2.38l-0.308 1H2a2 2 0 0 1 -2 -2V6a2 2 0 0 1 2 -2" stroke-width="1"></path>
|
|
342
342
|
<path d="M2 6h2.45L2.908 7.639A1.5 1.5 0 0 0 3.313 10H2zm8.595 -2 -0.308 1H12a1 1 0 0 1 1 1v4a1 1 0 0 1 -1 1H9.276l-0.942 1H12a2 2 0 0 0 2 -2V6a2 2 0 0 0 -2 -2z" stroke-width="1"></path>
|
|
343
343
|
<path d="M12 10h-1.783l1.542 -1.639q0.146 -0.156 0.241 -0.34zm0 -3.354V6h-0.646a1.5 1.5 0 0 1 0.646 0.646M16 8a1.5 1.5 0 0 1 -1.5 1.5v-3A1.5 1.5 0 0 1 16 8" stroke-width="1"></path>
|
|
@@ -348,6 +348,53 @@
|
|
|
348
348
|
<svg id="power_renewal" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24">
|
|
349
349
|
<path fill="currentColor" d="M4.06445 13C4.55672 16.9461 7.92051 20 12 20c2.3364 -0.0002 4.4372 -1.0031 5.8994 -2.6006L15.5 15h6v6l-2.1855 -2.1855C17.4894 20.7734 14.8887 21.9998 12 22c-5.18532 0 -9.44843 -3.9467 -9.9502 -9zM12.5 10H16l-4.5 9v-5H8l4.5 -9zM12 2c5.1851 0.00028 9.4485 3.94685 9.9502 9h-2.0147C19.4434 7.05399 16.0793 4.00027 12 4 9.66354 4 7.56264 5.00292 6.10059 6.60059L8.5 9h-6V3l2.18457 2.18457C6.50965 3.22563 9.11128 2 12 2" stroke-width="1"></path>
|
|
350
350
|
</svg>
|
|
351
|
+
<svg id="charger" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
352
|
+
<path fill="currentColor" fill-opacity="0.7" stroke="currentColor" stroke-opacity="0.5" stroke-width="0.2" d="m 18.526522,2.992101 c 0.580439,-0.01321 0.578699,0.00149 0.596108,-0.2465077 0.02012,-0.2470691 0.02494,-1.91517073 0.01988,-1.97978103 0.12148,-0.43521163 1.030866,-0.40307568 1.104701,-0.006281 -0.0061,0.0964339 0.0157,1.83374753 0.01125,1.99193083 0.01054,0.1942281 0.01127,0.1915155 0.236692,0.2282672 0.123095,0.0048 0.663877,0.046959 0.870775,0.071236 0.629908,0.078435 0.546705,0.7198795 0.184123,1.0560261 -0.280622,0.2614474 -0.478291,0.795547 -0.593006,1.6022987 -0.118811,0.8291615 -0.664733,1.3978094 -1.637762,1.7059437 -0.108951,0.033365 -0.182889,0.1266077 -0.184364,0.2325013 l -0.0092,1.3249778 c -6.99e-4,0.058661 -0.01761,0.1164398 -0.04916,0.1680732 -0.08194,0.1419286 -0.272448,0.211959 -0.571527,0.2100915 -0.301128,0 -0.491636,-0.070965 -0.571527,-0.2128928 -0.03157,-0.051631 -0.04849,-0.1094094 -0.0492,-0.1680731 V 7.6449295 C 17.884596,7.5387391 17.811826,7.4442875 17.703031,7.4096269 16.732051,7.0977576 16.189203,6.5263085 16.074488,5.6952795 15.965918,4.8885278 15.772337,4.3534946 15.493743,4.0901798 c -0.362582,-0.3389478 -0.427109,-0.949614 0.2028,-1.0224459 0.206897,-0.024278 0.524028,-0.032992 0.851764,-0.039526 0.236092,-0.013163 0.243529,-0.028409 0.250893,-0.2966214 -0.0019,-0.067609 0.02171,-1.91973584 0.01888,-1.9804634 0.130447,-0.40934157 1.03846,-0.42750873 1.115399,0.0164377 0.0056,0.0751203 -0.01932,1.7128202 -0.0031,1.9752302 0.02251,0.2158291 0.03941,0.2199654 0.596108,0.2493092 z" />
|
|
353
|
+
<path fill="currentColor" fill-opacity="0.7" stroke="currentColor" stroke-opacity="0.5" stroke-width="0.5" d="m 11.506042,7.8057483 c 0.107183,-0.0032 0.191857,-0.086257 0.190184,-0.1865545 C 11.687026,5.8381807 10.641016,5.6953499 8.8986916,5.68952 6.4569849,5.68174 4.6614917,5.709924 3.5122123,5.774052 2.0306237,5.858584 1.837373,6.8846346 1.8343055,8.1759419 c -0.00204,2.6001051 -6.194e-4,5.6507651 0.00427,9.8228741 -0.01077,0.569851 0.2418172,1.058016 0.5448099,1.265464 0.481593,0.32647 0.9904809,0.518667 1.0607401,0.771427 0.1216783,0.387457 0.1123871,0.577471 -0.1448346,0.903967 C 2.9574225,21.008744 2.5101741,20.797525 2.2238768,20.65178 0.61345188,19.829773 0.59403898,18.908625 0.60863288,16.952184 0.58951088,13.116111 0.58583788,10.506564 0.59449588,8.9828056 0.46872958,6.3214882 1.1379898,4.511894 4.183989,4.4914895 c 1.9999912,-0.013603 4.0050948,0.00292 6.015311,0.049553 1.83128,0.043724 2.558271,1.1397318 2.70551,2.7633394 0.02019,0.2208227 0.09035,0.4337311 0.205521,0.6237919 0.147238,0.2487394 0.24642,0.5120534 0.297544,0.7899419 0.03654,0.1905362 -0.118279,0.3656754 -0.322085,0.364365 L 9.1747636,9.0708215 C 8.8570456,9.0696334 8.6200346,8.7923263 8.6839686,8.4965832 l 0.0061,-0.023319 c 0.08615,-0.3642987 0.423096,-0.625504 0.815951,-0.632537 z" />
|
|
354
|
+
<path fill="currentColor" fill-opacity="1" stroke="var(--mat-sys-background)" stroke-opacity="0.5" stroke-width="1.25" d="m 23.341383,21.855011 a 2.148899,1.5884815 0 0 1 -2.148899,1.588482 H 6.8688978 A 2.148899,1.5884815 0 0 1 4.7199987,21.855011 V 12.345479 A 2.148899,1.5884815 0 0 1 6.8688978,10.756996 H 21.192484 a 2.148899,1.5884815 0 0 1 2.148899,1.588483 z" />
|
|
355
|
+
<path fill="var(--mat-sys-primary)" stroke="var(--mat-sys-background)" stroke-opacity="0.8" stroke-width="1.55" stroke-linejoin="round" stroke-linecap="butt" d="m 16.664469,10.746745 a 0.58489405,0.58489405 0 0 1 0.264373,0.678475 l -1.326539,4.309501 h 2.327878 a 0.58489405,0.58489405 0 0 1 0.425802,0.986131 l -6.239649,6.629189 a 0.58489405,0.58489405 0 0 1 -0.984963,-0.573196 l 1.32771,-4.310669 H 10.131203 A 0.58489405,0.58489405 0 0 1 9.7053994,17.480044 L 15.94388,10.850855 a 0.58489405,0.58489405 0 0 1 0.720589,-0.105283 z" />
|
|
356
|
+
</svg>
|
|
357
|
+
<svg id="inverter" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
358
|
+
<g transform="translate(-0.53766295,-0.11764124)">
|
|
359
|
+
<path fill="currentColor" stroke="none" stroke-width="1" d="M 5.2587761,0.79493449 A 1.6175304,1.6175304 0 0 0 3.6412457,2.4124751 H 1.2149499 V 3.22123 a 0.8087703,0.8087703 0 0 1 0,1.6175406 V 5.6475257 H 3.6412457 V 18.587757 H 1.2149499 v 0.808785 a 0.80876527,0.80876527 0 0 1 0,1.61751 v 0.808787 h 2.4262958 a 1.6175304,1.6175304 0 0 0 1.6175304,1.617509 H 19.81655 a 1.6175304,1.6175304 0 0 0 1.61753,-1.617509 h 2.426296 v -0.808787 a 0.80876527,0.80876527 0 0 1 0,-1.61751 V 18.587757 H 21.43408 V 5.6475257 h 2.426296 V 4.8387706 a 0.8087703,0.8087703 0 0 1 0,-1.6175406 V 2.4124751 H 21.43408 A 1.6175304,1.6175304 0 0 0 19.81655,0.79493449 Z" />
|
|
360
|
+
<rect fill="var(--mat-sys-background)" fill-opacity="0.7" width="14.700929" height="4.7658048" x="5.1283541" y="2.9362586" rx="0.22678168" ry="0.14382635" />
|
|
361
|
+
<path fill="var(--mat-sys-background)" fill-opacity="0.5" stroke="none" d="m 21.43408,19.396542 h 1.025175 a 1.6175304,1.6175304 0 0 0 0,1.61751 H 21.43408 Z" />
|
|
362
|
+
<path fill="var(--mat-sys-background)" fill-opacity="0.5" stroke="none" d="m 2.6160708,19.396542 h 1.0251749 v 1.61751 H 2.6160708 a 1.6175304,1.6175304 0 0 0 0,-1.61751 z" />
|
|
363
|
+
<path fill="var(--mat-sys-background)" fill-opacity="0.5" stroke="none" d="m 21.43408,3.22123 h 1.025175 a 1.6175304,1.6175304 0 0 0 0,1.6175406 H 21.43408 Z" />
|
|
364
|
+
<path fill="var(--mat-sys-background)" fill-opacity="0.5" stroke="none" d="M 2.6160708,3.22123 H 3.6412457 V 4.8387706 H 2.6160708 a 1.6175304,1.6175304 0 0 0 0,-1.6175406 z" />
|
|
365
|
+
</g>
|
|
366
|
+
<path fill="var(--mat-sys-primary)" stroke="var(--mat-sys-background)" stroke-opacity="0.8" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="butt" d="m 16.222725,13.289851 c 1.434346,2.625425 0.01816,5.899677 -2.877292,6.652372 l 0.671063,2.291434 -3.478775,-2.821079 c -0.443589,-1.025432 0.349519,-1.629526 1.002288,-1.487952 1.980739,0.62225 4.065616,-1.282791 2.927928,-3.642059 z" />
|
|
367
|
+
<path fill="var(--mat-sys-primary)" stroke="var(--mat-sys-background)" stroke-opacity="0.8" stroke-width="0.5" stroke-linejoin="round" stroke-linecap="butt" d="m 8.1473717,17.739509 c -1.4343461,-2.625425 -0.01816,-5.899677 2.8772923,-6.652372 l -0.671063,-2.2914343 3.478775,2.8210793 c 0.443589,1.025432 -0.349519,1.629526 -1.002288,1.487952 -1.980739,-0.62225 -4.0656163,1.282791 -2.9279283,3.642059 z" />
|
|
368
|
+
</svg>
|
|
369
|
+
<svg id="alternator" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
370
|
+
<path fill="currentColor" d="m 4.1744266,0.22283685 c 0.8577487,0 1.6494163,0.28025712 2.2953397,0.74961596 0.017761,0.01097 0.036306,0.0195893 0.053283,0.0321264 L 21.148396,11.973263 c 0.01071,0.0081 0.01881,0.01802 0.02899,0.02664 1.570015,1.193901 2.586306,3.080217 2.586306,5.200298 0,3.600507 -2.929249,6.529756 -6.529756,6.529756 -2.122693,0 -4.011359,-1.018642 -5.204738,-2.592313 -0.0068,-0.0084 -0.01541,-0.01437 -0.02194,-0.02298 L 1.0385767,6.489574 C 1.0257784,6.4725967 1.0174203,6.4540522 1.0064503,6.4362912 0.53683027,5.7903678 0.25657315,4.998439 0.25657315,4.1406903 c 0,-2.1603044 1.75754905,-3.91785345 3.91785345,-3.91785345 z M 17.233938,21.771031 c 2.520225,0 4.570829,-2.050605 4.570829,-4.570829 0,-2.520225 -2.050604,-4.570829 -4.570829,-4.570829 -2.520224,0 -4.570829,2.050604 -4.570829,4.570829 0,2.520224 2.050605,4.570829 4.570829,4.570829 z m -6.529756,-4.570307 c 0,0 0,-5.22e-4 0,-5.22e-4 0,-3.600508 2.929249,-6.529756 6.529756,-6.529756 2.61e-4,0 5.22e-4,0 5.22e-4,0 L 8.0753027,3.801143 c 0.0094,0.1120506 0.016977,0.2248848 0.016977,0.3395473 0,2.1603044 -1.757549,3.9178535 -3.9178534,3.9178535 -0.1146625,0 -0.2274967,-0.00757 -0.3395473,-0.017239 z M 4.1744266,6.099617 c 1.0802828,0 1.9589267,-0.8786439 1.9589267,-1.9589267 0,-1.0802828 -0.8786439,-1.9589267 -1.9589267,-1.9589267 -1.0802828,0 -1.9589267,0.8786439 -1.9589267,1.9589267 0,1.0802828 0.8786439,1.9589267 1.9589267,1.9589267 z" />
|
|
371
|
+
<path fill="currentColor" d="m 17.233938,14.5883 c 1.440203,0 2.611902,1.171699 2.611902,2.611902 0,1.440203 -1.171699,2.611902 -2.611902,2.611902 -1.440203,0 -2.611902,-1.171699 -2.611902,-2.611902 0,-1.440203 1.171699,-2.611902 2.611902,-2.611902 z m 0,3.917853 c 0.720102,0 1.305951,-0.58585 1.305951,-1.305951 0,-0.720102 -0.585849,-1.305951 -1.305951,-1.305951 -0.720101,0 -1.305951,0.585849 -1.305951,1.305951 0,0.720101 0.58585,1.305951 1.305951,1.305951 z" />
|
|
372
|
+
<circle fill="currentColor" opacity="0.8" cx="-19.808893" cy="14.623878" r="0.51125419" transform="scale(-1,1)" />
|
|
373
|
+
<circle fill="currentColor" opacity="0.8" cx="-17.148212" cy="13.600105" r="0.51125419" transform="scale(-1,1)" />
|
|
374
|
+
<circle fill="currentColor" opacity="0.8" cx="-13.631804" cy="17.14979" r="0.51125419" transform="scale(-1,1)" />
|
|
375
|
+
<circle fill="currentColor" opacity="0.8" cx="-14.605977" cy="19.607031" r="0.51125419" transform="scale(-1,1)" />
|
|
376
|
+
<circle fill="currentColor" opacity="0.8" cx="-17.162188" cy="20.772921" r="0.51125419" transform="scale(-1,1)" />
|
|
377
|
+
<circle fill="currentColor" opacity="0.8" cx="-19.747438" cy="19.743822" r="0.51125419" transform="scale(-1,1)" />
|
|
378
|
+
<circle fill="currentColor" opacity="0.8" id="path3-02" cx="-20.833885" cy="17.123131" r="0.51125419" transform="scale(-1,1)" />
|
|
379
|
+
<path fill="var(--mat-sys-primary)" stroke="var(--mat-sys-background)" stroke-opacity="0.8" stroke-width="1.55" stroke-linejoin="round" stroke-linecap="butt" d="m 15.9823,2.3965923 a 0.88436594,0.88436594 0 0 1 0.399735,1.0258611 l -2.005742,6.5160113 h 3.519777 a 0.88436594,0.88436594 0 0 1 0.643817,1.4910403 L 9.1054724,21.452908 A 0.88436594,0.88436594 0 0 1 7.6161975,20.58623 L 9.6237097,14.068454 H 6.103933 A 0.88436594,0.88436594 0 0 1 5.4601144,12.577413 L 14.892762,2.5540075 A 0.88436594,0.88436594 0 0 1 15.9823,2.3948186 Z" />
|
|
380
|
+
<circle fill="currentColor" fill-opacity="0.5" stroke="var(--mat-sys-background)" stroke-width="1.5" cx="4.1754789" cy="4.0798988" r="2.6573768" />
|
|
381
|
+
</svg>
|
|
382
|
+
<svg id="acMonitor" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
|
|
383
|
+
<path fill="currentColor" stroke="currentColor" stroke-width="0.8" d="m 12.254583,10.347123 3.128436,0.134257 h 0.09626 a 0.82580589,0.82580589 0 0 1 0.595289,0.430635 0.81314015,0.81314015 0 0 1 0,0.734612 l -3.921311,7.523447 a 0.60542211,0.60542211 0 0 1 -1.137383,-0.372373 l 0.732079,-4.450739 -3.1335025,-0.144389 h -0.09626 a 0.82580589,0.82580589 0 0 1 -0.5902232,-0.428102 0.8156733,0.8156733 0 0 1 0,-0.734613 L 11.84168,5.5265444 a 0.6104884,0.6104884 0 0 1 1.142449,0.3749058 z M 1.5140397,13.207046 a 1.2057779,1.2057779 0 0 0 1.2083111,-1.205778 9.2890497,9.2890497 0 0 1 9.2789172,-9.2789172 1.2070445,1.2070445 0 0 0 0,-2.41408899 A 11.705672,11.705672 0 0 0 0.30826182,12.001268 1.2057779,1.2057779 0 0 0 1.5140397,13.207046 Z m 2.4571525,3.450146 A 1.2083111,1.2083111 0 1 0 3.535491,18.308804 1.2057779,1.2057779 0 0 0 3.9737254,16.659725 Z M 21.0826,7.9608986 a 1.2083111,1.2083111 0 1 0 -1.04619,-0.602889 1.2007116,1.2007116 0 0 0 1.04619,0.602889 z M 6.17503,22.133855 A 1.2057779,1.2057779 0 1 0 5.7291962,20.484777 1.2133774,1.2133774 0 0 0 6.17503,22.133855 Z M 21.280185,12.001268 a 1.2057779,1.2057779 0 1 0 1.203245,-1.208311 1.2057779,1.2057779 0 0 0 -1.203245,1.208311 z m -0.795408,6.279671 a 1.2057779,1.2057779 0 1 0 -0.443301,-1.646545 v 0 a 1.2032448,1.2032448 0 0 0 0.443301,1.646545 z m -3.832651,1.752938 a 1.2083111,1.2083111 0 1 0 1.649078,0.435701 1.2057779,1.2057779 0 0 0 -1.649078,-0.435701 z m -5.838904,2.454619 a 1.2057779,1.2057779 0 1 0 1.203245,-1.208311 1.2057779,1.2057779 0 0 0 -1.203245,1.208311 z M 16.63946,3.9610596 A 1.2057779,1.2057779 0 1 0 16.196159,2.3145142 1.2032448,1.2032448 0 0 0 16.63946,3.9610596 Z" />
|
|
384
|
+
<path fill="var(--mat-sys-primary)" stroke="var(--mat-sys-background)" stroke-opacity="0.8" stroke-width="1.55" stroke-linejoin="round" stroke-linecap="butt" d="m 16.273919,2.4799121 a 0.88436594,0.88436594 0 0 1 0.399735,1.0258611 l -2.005742,6.5160108 h 3.519777 a 0.88436594,0.88436594 0 0 1 0.643817,1.491041 L 9.3970913,21.536228 A 0.88436594,0.88436594 0 0 1 7.9078168,20.66955 L 9.9153293,14.151774 H 6.3955523 A 0.88436594,0.88436594 0 0 1 5.7517337,12.660733 L 15.184381,2.6373273 a 0.88436594,0.88436594 0 0 1 1.089538,-0.1591889 z" />
|
|
385
|
+
</svg>
|
|
386
|
+
<svg id="solar_charger" fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" height="24" width="24">
|
|
387
|
+
<path fill="currentColor" stroke="currentColor" stroke-width="1" d="M7.666666666666667 2a3.8333333333333335 3.8333333333333335 0 0 0 7.666666666666667 0z"></path>
|
|
388
|
+
<path stroke="currentColor" d="M3.8333333333333335 2.875h0.9583333333333334" stroke-width="1.5"></path>
|
|
389
|
+
<path stroke="currentColor" d="M18.208333333333336 2.875h0.9583333333333334" stroke-width="1.5"></path>
|
|
390
|
+
<path stroke="currentColor" d="M11.5 8.625v0.9583333333333334" stroke-width="1.5"></path>
|
|
391
|
+
<path stroke="currentColor" d="m16.483333333333334 6.9 0.6775416666666667 0.6775416666666667" stroke-width="1.5"></path>
|
|
392
|
+
<path stroke="currentColor" d="m6.516666666666667 6.9 -0.6708333333333333 0.6708333333333333" stroke-width="1.5"></path>
|
|
393
|
+
<path fill="var(--mat-sys-primary)" fill-opacity="0.5" stroke="var(--mat-sys-primary)" stroke-opacity="1" d="M4.1016666666666675 20.125h14.796666666666667a0.9583333333333334 0.9583333333333334 0 0 0 0.9295833333333333 -1.1912083333333334l-1.4375 -5.75a0.9583333333333334 0.9583333333333334 0 0 0 -0.9295833333333333 -0.7254583333333333H5.5391666666666675a0.9583333333333334 0.9583333333333334 0 0 0 -0.9295833333333333 0.7254583333333333l-1.4375 5.75A0.9583333333333334 0.9583333333333334 0 0 0 4.1016666666666675 20.125z" stroke-width="1"></path>
|
|
394
|
+
<path stroke="var(--mat-sys-primary)" stroke-width="1" d="M3.8333333333333335 16.291666666666668h15.333333333333334"></path>
|
|
395
|
+
<path stroke="var(--mat-sys-primary)" stroke-width="1" d="m9.583333333333334 12.458333333333334 -0.9583333333333334 7.666666666666667"></path>
|
|
396
|
+
<path stroke="var(--mat-sys-primary)" stroke-width="1" d="m13.416666666666668 12.458333333333334 0.9583333333333334 7.666666666666667"></path>
|
|
397
|
+
</svg>
|
|
351
398
|
<svg id="dashboard-beating-starboard" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
|
|
352
399
|
<g id="g9" transform="matrix(-0.70710678,0.70710678,0.70710678,0.70710678,8.8211956,-6.2237242)">
|
|
353
400
|
<path d="M 12,5.9999999 C 16,9.777778 17.777778,15.444445 16,23 H 8.0000001 C 6.2222223,15.444445 8.0000001,9.777778 12,5.9999999 Z" fill="var(--mat-sys-tertiary)" id="path1" style="stroke-width:0.229061" />
|