speedandcurrent 2.3.3 → 2.3.4
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/index.js +14 -5
- package/package.json +2 -2
- package/public/app.js +114 -98
package/index.js
CHANGED
|
@@ -327,6 +327,8 @@ module.exports = function (app) {
|
|
|
327
327
|
const safePath = path || 'unknown path';
|
|
328
328
|
const message = status === 'idle'
|
|
329
329
|
? `Input ${id} is idle on ${safePath}; resubscribing`
|
|
330
|
+
: status === 'incomplete'
|
|
331
|
+
? `Input ${id} is missing ${safePath}`
|
|
330
332
|
: `Input ${id} is stale on ${safePath}`;
|
|
331
333
|
lifecycleWarningMap.set(id, {
|
|
332
334
|
id,
|
|
@@ -632,7 +634,7 @@ module.exports = function (app) {
|
|
|
632
634
|
app, pluginId: plugin.id,
|
|
633
635
|
id: 'boatSpeed',
|
|
634
636
|
path: 'navigation.speedThroughWater',
|
|
635
|
-
subscribe:
|
|
637
|
+
subscribe: false,
|
|
636
638
|
SmootherClass,
|
|
637
639
|
smootherOptions,
|
|
638
640
|
onDelta: () => {
|
|
@@ -648,7 +650,7 @@ module.exports = function (app) {
|
|
|
648
650
|
now: Date.now()
|
|
649
651
|
});
|
|
650
652
|
|
|
651
|
-
const wellUnderway =
|
|
653
|
+
const wellUnderway = Date.now() >= learningStabilizingUntil;
|
|
652
654
|
setStatus(learningMode.state === 'stabilizing' ? 'Stabilizing' : 'Running');
|
|
653
655
|
if (options.estimateBoatSpeed) correct(wellUnderway);
|
|
654
656
|
updateTable();
|
|
@@ -766,6 +768,7 @@ module.exports = function (app) {
|
|
|
766
768
|
lastNavigationStateValue = normalizeNavigationState(navigationStateHandler?.value);
|
|
767
769
|
resetLearningStabilization('startup', LONG_STABILIZING_MS);
|
|
768
770
|
setStatus('Running');
|
|
771
|
+
smoothedBoatSpeed.subscribe();
|
|
769
772
|
app.debug("Running");
|
|
770
773
|
|
|
771
774
|
}
|
|
@@ -841,7 +844,8 @@ module.exports = function (app) {
|
|
|
841
844
|
if (options.sogFallback && rawGroundSpeed.ready && rawBoatSpeed.ready && rawBoatSpeed.value === 0 && rawGroundSpeed.magnitude >= minSpeed) {
|
|
842
845
|
correctedBoatSpeed.setVectorValue({ x: rawGroundSpeed.magnitude, y: 0 });
|
|
843
846
|
}
|
|
844
|
-
else if (rawAttitude.ready) {
|
|
847
|
+
else if (rawAttitude.ready && Number.isFinite(rawAttitude.value?.roll)) {
|
|
848
|
+
clearLifecycleWarning('attitude.roll');
|
|
845
849
|
if (correctedBoatSpeed.magnitude > 0) {
|
|
846
850
|
const { correction, variance } = table.getCorrection(correctedBoatSpeed.magnitude, rawAttitude.value?.roll);
|
|
847
851
|
const leewayValid = isLeewayValid(correctedBoatSpeed.magnitude, minSpeed, navigationStateHandler);
|
|
@@ -877,7 +881,10 @@ module.exports = function (app) {
|
|
|
877
881
|
}
|
|
878
882
|
}
|
|
879
883
|
}
|
|
880
|
-
// Implicit fallback: attitude not ready — correctedBoatSpeed = raw STW, no correction
|
|
884
|
+
// Implicit fallback: attitude not ready or missing roll — correctedBoatSpeed = raw STW, no correction
|
|
885
|
+
else if (rawAttitude.ready) {
|
|
886
|
+
setLifecycleWarning('attitude.roll', 'incomplete', 'navigation.attitude.roll');
|
|
887
|
+
}
|
|
881
888
|
|
|
882
889
|
PolarSmoother.send(app, plugin.id, [smoothedCurrent, smoothedResidual]);
|
|
883
890
|
|
|
@@ -902,7 +909,8 @@ module.exports = function (app) {
|
|
|
902
909
|
learningMode.state = 'suspended';
|
|
903
910
|
learningMode.reason = 'cog_override';
|
|
904
911
|
}
|
|
905
|
-
const inputsReady = smoothedAttitude.ready &&
|
|
912
|
+
const inputsReady = smoothedAttitude.ready && Number.isFinite(smoothedAttitude.value?.roll)
|
|
913
|
+
&& smoothedBoatSpeed.ready && smoothedHeading.ready && smoothedGroundSpeed.ready;
|
|
906
914
|
const currentReady = !options.assumeCurrent || smoothedCurrent.ready;
|
|
907
915
|
const observationGate = evaluateObservationGate({
|
|
908
916
|
learningMode,
|
|
@@ -916,6 +924,7 @@ module.exports = function (app) {
|
|
|
916
924
|
|
|
917
925
|
if (observationGate.state !== 'pending') {
|
|
918
926
|
if (observationGate.state === 'invalid') {
|
|
927
|
+
setObservationStatus('rejected', observationGate.reason);
|
|
919
928
|
resetLearningStabilization('observation_reset', getShortStabilizingMs(options));
|
|
920
929
|
}
|
|
921
930
|
return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "speedandcurrent",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.4",
|
|
4
4
|
"description": "A plugin that automatically corrects boat speed and calculates current and leeway using sensor fusion.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"kalman-filter": "^2.3.0",
|
|
44
|
-
"signalkutilities": "^3.1.
|
|
44
|
+
"signalkutilities": "^3.1.5"
|
|
45
45
|
},
|
|
46
46
|
"files": [
|
|
47
47
|
"icon.png",
|
package/public/app.js
CHANGED
|
@@ -97,7 +97,6 @@ function normaliseState(data) {
|
|
|
97
97
|
|
|
98
98
|
// ─── Static meta (fetched once at startup from /api/meta) ─────────────────────
|
|
99
99
|
let metaById = {}; // keyed by item id
|
|
100
|
-
let lifecycleWarnings = [];
|
|
101
100
|
|
|
102
101
|
async function loadMeta() {
|
|
103
102
|
const data = await apiGet('/api/meta');
|
|
@@ -117,90 +116,99 @@ async function loadMeta() {
|
|
|
117
116
|
applyUnitLabels();
|
|
118
117
|
}
|
|
119
118
|
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
function isNotReady(item) {
|
|
126
|
-
if (!item) return true;
|
|
127
|
-
return item.state?.ready !== true;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
// Returns a human-readable reason why an item is not ready.
|
|
131
|
-
// Handles three state shapes:
|
|
132
|
-
// handler-nested (MessageSmoother, SmoothedAngle): state.handler.*
|
|
133
|
-
// magnitude/angle (PolarSmoother): state.magnitude.* / state.angle.*
|
|
134
|
-
// flat top-level (MessageHandler, Polar): state.*
|
|
135
|
-
function getNotReadyReason(item) {
|
|
136
|
-
if (!item) return 'not available';
|
|
137
|
-
const s = item.state;
|
|
138
|
-
if (!s) return 'not available';
|
|
119
|
+
const computedItemIds = new Set([
|
|
120
|
+
'current.smoothed', 'boatSpeedRefGround', 'speedCorrection',
|
|
121
|
+
'correctedBoatSpeed', 'residual', 'residual.smoothed'
|
|
122
|
+
]);
|
|
139
123
|
|
|
140
|
-
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
|
|
124
|
+
function getPathStatus(pathState, hasValue, computed = false, computedReady = false) {
|
|
125
|
+
if (computed) {
|
|
126
|
+
return hasValue && computedReady
|
|
127
|
+
? { category: 'computed', reason: '' }
|
|
128
|
+
: { category: 'inactive', reason: 'not computed' };
|
|
129
|
+
}
|
|
130
|
+
if (pathState?.subscribed === false) {
|
|
131
|
+
return { category: 'inactive', reason: 'not subscribed' };
|
|
132
|
+
}
|
|
133
|
+
if (hasValue && pathState?.ready === true) {
|
|
134
|
+
return { category: 'subscribed', reason: '' };
|
|
135
|
+
}
|
|
136
|
+
if (!pathState) {
|
|
137
|
+
return { category: 'problem', reason: 'not available' };
|
|
138
|
+
}
|
|
139
|
+
if (pathState.pathKnown === false) {
|
|
140
|
+
return { category: 'problem', reason: 'path not found in Signal K' };
|
|
141
|
+
}
|
|
142
|
+
if (pathState.hasDelta === false) {
|
|
143
|
+
return { category: 'problem', reason: 'waiting for first data' };
|
|
144
|
+
}
|
|
145
|
+
if (pathState.isStale === true) {
|
|
146
|
+
return { category: 'problem', reason: 'data is stale' };
|
|
147
|
+
}
|
|
148
|
+
return { category: 'problem', reason: 'not available' };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function getScalarPathState(state) {
|
|
152
|
+
if (!state?.handler) return state;
|
|
153
|
+
return {
|
|
154
|
+
...state,
|
|
155
|
+
subscribed: state.handler.subscribed,
|
|
156
|
+
pathKnown: state.handler.pathKnown,
|
|
157
|
+
hasDelta: state.hasDelta === true && state.handler.hasDelta === true,
|
|
158
|
+
isStale: state.isStale === true || state.handler.isStale === true,
|
|
159
|
+
ready: state.ready === true && state.handler.ready === true
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
function getItemStatuses(item) {
|
|
164
|
+
const label = itemLabel(item);
|
|
165
|
+
const computed = computedItemIds.has(item.id);
|
|
166
|
+
const computedReady = item.state?.ready === true;
|
|
167
|
+
if (state.polarsById[item.id] === item) {
|
|
168
|
+
return [
|
|
169
|
+
{
|
|
170
|
+
label: `${label} magnitude`,
|
|
171
|
+
status: getPathStatus(item.state?.magnitude, Number.isFinite(item.magnitude), computed, computedReady)
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
label: `${label} angle`,
|
|
175
|
+
status: getPathStatus(
|
|
176
|
+
item.state?.angle,
|
|
177
|
+
Number.isFinite(item.angle),
|
|
178
|
+
computed || item.state?.angleFallbackActive === true,
|
|
179
|
+
computedReady
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
];
|
|
151
183
|
}
|
|
152
184
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function getAnyItem(id) {
|
|
161
|
-
return state.polarsById[id] || state.deltasById[id] || state.attitudesById[id] || null;
|
|
185
|
+
const pathState = getScalarPathState(item.state);
|
|
186
|
+
const hasValue = state.attitudesById[item.id] === item
|
|
187
|
+
? Number.isFinite(item.value?.roll) || Number.isFinite(item.value?.pitch)
|
|
188
|
+
: Number.isFinite(item.value);
|
|
189
|
+
return [{ label, status: getPathStatus(pathState, hasValue, computed, computedReady) }];
|
|
162
190
|
}
|
|
163
191
|
|
|
164
|
-
function
|
|
165
|
-
|
|
166
|
-
if (!el) return;
|
|
167
|
-
el.innerHTML = '';
|
|
168
|
-
const notReady = ids.filter(id => isNotReady(getAnyItem(id)));
|
|
169
|
-
if (notReady.length === 0) return;
|
|
170
|
-
const h = document.createElement('h6');
|
|
171
|
-
h.className = 'text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small';
|
|
172
|
-
h.textContent = 'Warnings';
|
|
173
|
-
el.appendChild(h);
|
|
174
|
-
const ul = document.createElement('ul');
|
|
175
|
-
ul.className = 'list-unstyled text-danger small ps-3';
|
|
176
|
-
notReady.forEach(id => {
|
|
177
|
-
const item = getAnyItem(id);
|
|
178
|
-
const label = metaById[id]?.displayName ?? id;
|
|
179
|
-
const li = document.createElement('li');
|
|
180
|
-
li.textContent = `"${label}" — ${getNotReadyReason(item)}`;
|
|
181
|
-
ul.appendChild(li);
|
|
182
|
-
});
|
|
183
|
-
el.appendChild(ul);
|
|
192
|
+
function isAvailableStatus(status) {
|
|
193
|
+
return status.category === 'subscribed' || status.category === 'computed';
|
|
184
194
|
}
|
|
185
195
|
|
|
186
|
-
function
|
|
196
|
+
function renderWarnings(elId, items) {
|
|
187
197
|
const el = document.getElementById(elId);
|
|
188
198
|
if (!el) return;
|
|
189
199
|
el.innerHTML = '';
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
});
|
|
194
|
-
if (lines.length === 0) return;
|
|
200
|
+
const problems = items.flatMap(item => getItemStatuses(item))
|
|
201
|
+
.filter(({ status }) => status.category === 'problem');
|
|
202
|
+
if (problems.length === 0) return;
|
|
195
203
|
const h = document.createElement('h6');
|
|
196
204
|
h.className = 'text-uppercase fw-bold text-muted border-bottom pb-1 mt-3 mb-1 small';
|
|
197
205
|
h.textContent = 'Warnings';
|
|
198
206
|
el.appendChild(h);
|
|
199
207
|
const ul = document.createElement('ul');
|
|
200
208
|
ul.className = 'list-unstyled text-danger small ps-3';
|
|
201
|
-
|
|
209
|
+
problems.forEach(({ label, status }) => {
|
|
202
210
|
const li = document.createElement('li');
|
|
203
|
-
li.textContent =
|
|
211
|
+
li.textContent = `"${label}" — ${status.reason}`;
|
|
204
212
|
ul.appendChild(li);
|
|
205
213
|
});
|
|
206
214
|
el.appendChild(ul);
|
|
@@ -447,18 +455,24 @@ function renderSettingsPanel() {
|
|
|
447
455
|
|
|
448
456
|
function formatPolarValue(p) {
|
|
449
457
|
if (!p) return '—';
|
|
458
|
+
const statuses = getItemStatuses(p);
|
|
450
459
|
const m = metaById[p.id];
|
|
451
460
|
const speedC = buildConverter(m?.magnitude?.displayUnits) || DEFAULTS.speed;
|
|
452
461
|
const angleC = buildConverter(m?.angle?.displayUnits) || DEFAULTS.angle;
|
|
453
|
-
const spd =
|
|
454
|
-
|
|
455
|
-
|
|
462
|
+
const spd = isAvailableStatus(statuses[0].status)
|
|
463
|
+
? `${speedC.convert(p.magnitude).toFixed(speedC.decimals)} ${speedC.symbol}`
|
|
464
|
+
: statuses[0].status.reason;
|
|
465
|
+
const ang = isAvailableStatus(statuses[1].status)
|
|
466
|
+
? `${angleC.convert(p.angle).toFixed(angleC.decimals)}${angleC.symbol}`
|
|
467
|
+
: statuses[1].status.reason;
|
|
468
|
+
const sigma = (isAvailableStatus(statuses[0].status) && config?.showStatistics && p.id.endsWith('.smoothed') && typeof p.trace === 'number')
|
|
456
469
|
? ` (σ=${speedC.convert(Math.sqrt(p.trace)).toFixed(speedC.decimals + 2)})` : '';
|
|
457
|
-
return `${spd}
|
|
470
|
+
return `${spd}${sigma} / ${ang}`;
|
|
458
471
|
}
|
|
459
472
|
|
|
460
473
|
function formatDeltaValue(d) {
|
|
461
|
-
|
|
474
|
+
const status = getItemStatuses(d)[0].status;
|
|
475
|
+
if (!isAvailableStatus(status)) return status.reason;
|
|
462
476
|
const m = metaById[d.id];
|
|
463
477
|
const uc = buildConverter(m?.displayUnits)
|
|
464
478
|
|| (m?.units === 'm/s' ? DEFAULTS.speed : DEFAULTS.angle);
|
|
@@ -468,6 +482,8 @@ function formatDeltaValue(d) {
|
|
|
468
482
|
}
|
|
469
483
|
|
|
470
484
|
function formatAttitudeValue(a) {
|
|
485
|
+
const status = getItemStatuses(a)[0].status;
|
|
486
|
+
if (!isAvailableStatus(status)) return status.reason;
|
|
471
487
|
const v = (a && a.value) || {};
|
|
472
488
|
const variance = a && a.variance;
|
|
473
489
|
const m = metaById[a?.id];
|
|
@@ -490,7 +506,6 @@ function buildDataTable(rows) {
|
|
|
490
506
|
const tbody = document.createElement('tbody');
|
|
491
507
|
rows.forEach(row => {
|
|
492
508
|
const tr = document.createElement('tr');
|
|
493
|
-
if (row.stale) tr.className = 'stale';
|
|
494
509
|
const tdL = document.createElement('td'); tdL.textContent = row.label;
|
|
495
510
|
const tdV = document.createElement('td'); tdV.textContent = row.value;
|
|
496
511
|
tr.appendChild(tdL); tr.appendChild(tdV);
|
|
@@ -518,9 +533,9 @@ function renderGroupInto(elId, polars, deltas, attitudes) {
|
|
|
518
533
|
if (!el) return;
|
|
519
534
|
el.innerHTML = '';
|
|
520
535
|
const rows = [
|
|
521
|
-
...polars .map(p => ({ label: itemLabel(p), value: formatPolarValue(p)
|
|
522
|
-
...deltas .map(d => ({ label: itemLabel(d), value: formatDeltaValue(d)
|
|
523
|
-
...attitudes.map(a => ({ label: itemLabel(a), value: formatAttitudeValue(a)
|
|
536
|
+
...polars .map(p => ({ label: itemLabel(p), value: formatPolarValue(p) })),
|
|
537
|
+
...deltas .map(d => ({ label: itemLabel(d), value: formatDeltaValue(d) })),
|
|
538
|
+
...attitudes.map(a => ({ label: itemLabel(a), value: formatAttitudeValue(a) }))
|
|
524
539
|
];
|
|
525
540
|
if (rows.length) el.appendChild(buildDataTable(rows));
|
|
526
541
|
}
|
|
@@ -533,6 +548,7 @@ function renderLiveSections() {
|
|
|
533
548
|
const fallbackInputPolars = inputPolars.length ? inputPolars : filterById(state.polarsAll, ['groundSpeed.smoothed']);
|
|
534
549
|
const fallbackInputDeltas = inputDeltas.length ? inputDeltas : filterById(state.deltasAll, ['heading.smoothed', 'boatSpeed.smoothed']);
|
|
535
550
|
const fallbackInputAttitudes = inputAttitudes.length ? inputAttitudes : filterById(state.attitudesAll, ['attitude.smoothed']);
|
|
551
|
+
const displayedInputs = [...fallbackInputPolars, ...fallbackInputDeltas, ...fallbackInputAttitudes];
|
|
536
552
|
|
|
537
553
|
// Inputs section — raw sensor readings only (smoothing is internal to the plugin)
|
|
538
554
|
renderGroupInto('inputs-values',
|
|
@@ -540,43 +556,44 @@ function renderLiveSections() {
|
|
|
540
556
|
fallbackInputDeltas,
|
|
541
557
|
fallbackInputAttitudes
|
|
542
558
|
);
|
|
543
|
-
|
|
544
|
-
if (config && config.assumeCurrent) inputWarningIds.push('current.smoothed');
|
|
545
|
-
renderInputWarnings('inputs-warnings', inputWarningIds);
|
|
559
|
+
renderWarnings('inputs-warnings', displayedInputs);
|
|
546
560
|
|
|
547
561
|
// Estimation — inputs (raw sensor data used for boat speed estimation)
|
|
562
|
+
const estimationInputs = [
|
|
563
|
+
...filterById(state.polarsAll, ['groundSpeed']),
|
|
564
|
+
...filterById(state.deltasAll, ['heading.angle', 'boatSpeed']),
|
|
565
|
+
...filterById(state.attitudesAll, ['attitude'])
|
|
566
|
+
];
|
|
548
567
|
renderGroupInto('estimation-inputs',
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
568
|
+
estimationInputs.filter(item => state.polarsById[item.id] === item),
|
|
569
|
+
estimationInputs.filter(item => state.deltasById[item.id] === item),
|
|
570
|
+
estimationInputs.filter(item => state.attitudesById[item.id] === item)
|
|
552
571
|
);
|
|
553
572
|
// Estimation — intermediates
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
[], []
|
|
557
|
-
);
|
|
573
|
+
const estimationIntermediates = filterById(state.polarsAll, ['boatSpeedRefGround', 'speedCorrection', 'residual', 'residual.smoothed']);
|
|
574
|
+
renderGroupInto('estimation-intermediates', estimationIntermediates, [], []);
|
|
558
575
|
// Estimation — outputs
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
[], []
|
|
562
|
-
);
|
|
576
|
+
const estimationOutputs = filterById(state.polarsAll, ['correctedBoatSpeed', 'current.smoothed']);
|
|
577
|
+
renderGroupInto('estimation-outputs', estimationOutputs, [], []);
|
|
563
578
|
// Estimation — warnings
|
|
564
|
-
|
|
565
|
-
if (estimationWarnings) estimationWarnings.innerHTML = '';
|
|
579
|
+
renderWarnings('estimation-warnings', [...estimationInputs, ...estimationIntermediates, ...estimationOutputs]);
|
|
566
580
|
|
|
567
581
|
// Learning — inputs: smoothed sensors + current if assumeCurrent
|
|
568
582
|
const learningCurrentPolars = (config && config.assumeCurrent)
|
|
569
583
|
? filterById(state.polarsAll, ['current.smoothed'])
|
|
570
584
|
: [];
|
|
585
|
+
const learningPolars = [...filterById(state.polarsAll, ['groundSpeed.smoothed']), ...learningCurrentPolars];
|
|
586
|
+
const learningDeltas = filterById(state.deltasAll, ['heading.smoothed', 'boatSpeed.smoothed']);
|
|
587
|
+
const learningAttitudes = filterById(state.attitudesAll, ['attitude.smoothed']);
|
|
571
588
|
renderGroupInto('learning-inputs',
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
589
|
+
learningPolars,
|
|
590
|
+
learningDeltas,
|
|
591
|
+
learningAttitudes
|
|
575
592
|
);
|
|
576
593
|
// Learning — warnings
|
|
577
594
|
const learningWarnings = document.getElementById('learning-warnings');
|
|
595
|
+
renderWarnings('learning-warnings', [...learningPolars, ...learningDeltas, ...learningAttitudes]);
|
|
578
596
|
if (learningWarnings) {
|
|
579
|
-
learningWarnings.innerHTML = '';
|
|
580
597
|
const navState = learningState?.navigationState;
|
|
581
598
|
if (navState?.enabled && navState.pathKnown === false) {
|
|
582
599
|
const ul = document.createElement('ul');
|
|
@@ -675,7 +692,6 @@ async function tick() {
|
|
|
675
692
|
.then(r => r.ok ? r.json() : null)
|
|
676
693
|
.catch(() => null);
|
|
677
694
|
_pluginStatus = statusData?.status ?? '';
|
|
678
|
-
lifecycleWarnings = Array.isArray(statusData?.lifecycleWarnings) ? statusData.lifecycleWarnings : [];
|
|
679
695
|
_refreshMessage();
|
|
680
696
|
}
|
|
681
697
|
|