@rhizomatics/signalk-einklabel-plugin 0.9.0-beta1 → 0.9.0-beta2
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/dist/config.d.ts +10 -11
- package/dist/config.js +35 -2
- package/dist/plugin.js +1 -0
- package/dist/repaintScheduler.js +11 -0
- package/package.json +1 -1
package/dist/config.d.ts
CHANGED
|
@@ -94,19 +94,18 @@ export interface PluginConfig {
|
|
|
94
94
|
*/
|
|
95
95
|
export declare const BUNDLED_TEMPLATES_DIR: string;
|
|
96
96
|
export declare function defaultConfig(): PluginConfig;
|
|
97
|
+
export declare function readCurrentConfig(app: ServerAPI): Partial<PluginConfig>;
|
|
97
98
|
/**
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
* `
|
|
102
|
-
*
|
|
103
|
-
* `
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
* `clearForceRepaint` in `./repaintScheduler.ts`). Unwrapping any such nesting and keeping only recognised
|
|
107
|
-
* fields here means every save collapses back down instead of growing, self-healing an already-corrupted file.
|
|
99
|
+
* Actively rewrites the on-disk file once it's nested (see `readCurrentConfig`'s doc comment) -
|
|
100
|
+
* `readCurrentConfig` alone only self-heals in memory for callers that go through it, but the admin
|
|
101
|
+
* UI's own config-editing form round-trips whatever raw JSON it was handed verbatim, including a
|
|
102
|
+
* stray nested `configuration` key it never touches (no schema field maps to it) - so left alone,
|
|
103
|
+
* every future save from the UI keeps re-persisting that dead weight forever (see
|
|
104
|
+
* `support/signalk-einklabel-plugin.json`). Called once at plugin start, which - unlike
|
|
105
|
+
* `clearForceRepaint` - isn't gated on any device having `forceRepaint` set, so a nested file gets
|
|
106
|
+
* flattened even if nothing ever triggers that path.
|
|
108
107
|
*/
|
|
109
|
-
export declare function
|
|
108
|
+
export declare function healNestedConfig(app: ServerAPI): void;
|
|
110
109
|
/**
|
|
111
110
|
* Resolves the user-facing `templatesDir` setting to an actual directory, mirroring
|
|
112
111
|
* signalk-parquet's `outputDirectory` convention: empty means the default location, a relative
|
package/dist/config.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.BUNDLED_TEMPLATES_DIR = exports.ALL_DEVICES = void 0;
|
|
4
4
|
exports.defaultConfig = defaultConfig;
|
|
5
5
|
exports.readCurrentConfig = readCurrentConfig;
|
|
6
|
+
exports.healNestedConfig = healNestedConfig;
|
|
6
7
|
exports.resolveTemplatesDir = resolveTemplatesDir;
|
|
7
8
|
exports.parseDevice = parseDevice;
|
|
8
9
|
exports.resolveTemplatePath = resolveTemplatePath;
|
|
@@ -65,14 +66,21 @@ const PLUGIN_CONFIG_KEYS = [
|
|
|
65
66
|
* `clearForceRepaint` in `./repaintScheduler.ts`). Unwrapping any such nesting and keeping only recognised
|
|
66
67
|
* fields here means every save collapses back down instead of growing, self-healing an already-corrupted file.
|
|
67
68
|
*/
|
|
68
|
-
function
|
|
69
|
-
let
|
|
69
|
+
function unwrapNestedConfiguration(raw) {
|
|
70
|
+
let levels = 0;
|
|
70
71
|
while (raw &&
|
|
71
72
|
typeof raw === "object" &&
|
|
72
73
|
"configuration" in raw &&
|
|
73
74
|
typeof raw.configuration === "object") {
|
|
74
75
|
raw = raw.configuration;
|
|
76
|
+
levels++;
|
|
75
77
|
}
|
|
78
|
+
// One level is the normal, expected shape signalk-server itself stores on disk (see the doc
|
|
79
|
+
// comment above) - only a *second* nested `configuration` key onwards is the corruption this
|
|
80
|
+
// guards against.
|
|
81
|
+
return { unwrapped: raw, wasNested: levels > 1 };
|
|
82
|
+
}
|
|
83
|
+
function pickKnownKeys(raw) {
|
|
76
84
|
const result = {};
|
|
77
85
|
if (raw && typeof raw === "object") {
|
|
78
86
|
for (const key of PLUGIN_CONFIG_KEYS) {
|
|
@@ -83,6 +91,31 @@ function readCurrentConfig(app) {
|
|
|
83
91
|
}
|
|
84
92
|
return result;
|
|
85
93
|
}
|
|
94
|
+
function readCurrentConfig(app) {
|
|
95
|
+
const { unwrapped } = unwrapNestedConfiguration(app.readPluginOptions());
|
|
96
|
+
return pickKnownKeys(unwrapped);
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Actively rewrites the on-disk file once it's nested (see `readCurrentConfig`'s doc comment) -
|
|
100
|
+
* `readCurrentConfig` alone only self-heals in memory for callers that go through it, but the admin
|
|
101
|
+
* UI's own config-editing form round-trips whatever raw JSON it was handed verbatim, including a
|
|
102
|
+
* stray nested `configuration` key it never touches (no schema field maps to it) - so left alone,
|
|
103
|
+
* every future save from the UI keeps re-persisting that dead weight forever (see
|
|
104
|
+
* `support/signalk-einklabel-plugin.json`). Called once at plugin start, which - unlike
|
|
105
|
+
* `clearForceRepaint` - isn't gated on any device having `forceRepaint` set, so a nested file gets
|
|
106
|
+
* flattened even if nothing ever triggers that path.
|
|
107
|
+
*/
|
|
108
|
+
function healNestedConfig(app) {
|
|
109
|
+
const { unwrapped, wasNested } = unwrapNestedConfiguration(app.readPluginOptions());
|
|
110
|
+
if (!wasNested)
|
|
111
|
+
return;
|
|
112
|
+
app.savePluginOptions(pickKnownKeys(unwrapped), (err) => {
|
|
113
|
+
if (err)
|
|
114
|
+
app.debug(`failed to clean up legacy nested plugin config: ${err.message}`);
|
|
115
|
+
else
|
|
116
|
+
app.debug("cleaned up a legacy nested plugin config file on disk");
|
|
117
|
+
});
|
|
118
|
+
}
|
|
86
119
|
/**
|
|
87
120
|
* Resolves the user-facing `templatesDir` setting to an actual directory, mirroring
|
|
88
121
|
* signalk-parquet's `outputDirectory` convention: empty means the default location, a relative
|
package/dist/plugin.js
CHANGED
|
@@ -49,6 +49,7 @@ function createPlugin(app) {
|
|
|
49
49
|
...config,
|
|
50
50
|
};
|
|
51
51
|
app.debug(`starting with ${pluginConfig.devices.length} configured device(s)`);
|
|
52
|
+
(0, config_1.healNestedConfig)(app);
|
|
52
53
|
if (pluginConfig.scanOnStart) {
|
|
53
54
|
void runStartupScan(app, pluginConfig.scanDurationSeconds);
|
|
54
55
|
}
|
package/dist/repaintScheduler.js
CHANGED
|
@@ -234,11 +234,22 @@ async function considerRepaint(app, config, device, target, state, getApiUrl) {
|
|
|
234
234
|
});
|
|
235
235
|
const rawContext = await assembleRawContext(app, apiUrl, bindings);
|
|
236
236
|
const templateHash = hashTemplate(templateMtimeMs);
|
|
237
|
+
// Hashed from `rawContext`, not `renderContext` below - `meta.repainted` (and the rest of `meta`)
|
|
238
|
+
// is only added after this point, deliberately, so a template merely *displaying* the repaint
|
|
239
|
+
// timestamp doesn't perpetually invalidate its own dedup and force a repaint every check. A full
|
|
240
|
+
// paint flashes the whole panel several times, and there's no confirmed partial-refresh path on
|
|
241
|
+
// these devices - so a bound value simply being unchanged is worth trusting over any staleness in
|
|
242
|
+
// a displayed clock.
|
|
237
243
|
const dataHash = hashData(rawContext);
|
|
238
244
|
const stateKey = stateKeyFor(device.friendlyName, address);
|
|
239
245
|
const previous = state[stateKey];
|
|
240
246
|
const templateChanged = previous?.templateHash !== templateHash;
|
|
241
247
|
const dataChanged = previous?.dataHash !== dataHash;
|
|
248
|
+
// The devices are battery-constrained, so a repaint is skipped whenever neither the template nor
|
|
249
|
+
// the bound data has changed since the last successful paint - regardless of what triggered this
|
|
250
|
+
// check (interval or subscription) or whether it's a regular scheduled tick vs. the deferred
|
|
251
|
+
// startup catch-up (see `startupCheckTimer`). `forceRepaint` is the explicit, one-shot override
|
|
252
|
+
// for "repaint anyway".
|
|
242
253
|
if (!templateChanged && !dataChanged && !device.forceRepaint) {
|
|
243
254
|
app.debug(`${label}: data unchanged, skipping repaint`);
|
|
244
255
|
return;
|