@rhizomatics/signalk-einklabel-plugin 0.9.0-beta1 → 0.9.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 -4
- package/README.md +11 -4
- 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/CHANGELOG.md
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
# 0.9.0
|
|
2
2
|
|
|
3
|
+
## Tide Clock Example
|
|
3
4
|
- Rename `third_quarter` to `last_quarter` for moon phase icons in `templates/.assets/lunar_phases` to match Derived Data plugin
|
|
4
5
|
- `tide.svg` renamed to `tides\416x240-BWRY.svg`. Original maintained but labelled as deprecated
|
|
5
|
-
- Added a simpler 250x128 version of tide clock for 2.13" labels
|
|
6
|
+
- Added a simpler *250x128* version of tide clock for 2.13" labels
|
|
7
|
+
## Fixes
|
|
6
8
|
- Fix configuration JSON holding older versions of itself as subentries
|
|
7
|
-
|
|
9
|
+
and data changes
|
|
8
10
|
- Fix most cases of scanned devices not appearing in device dropdown choice
|
|
11
|
+
## Improvements
|
|
12
|
+
- Repaint state used to track which labels to repaint separately tracks the template
|
|
9
13
|
- `nearestColour` algorithm in Zhsunyco driver supports ESLs that only have BWR or BW
|
|
10
|
-
|
|
11
|
-
|
|
14
|
+
## Device Selection
|
|
15
|
+
- New 'ALL' as device option, and by default disable initial scan, to optimize support for single devices, so can configure and go without waiting for scan
|
|
16
|
+
- Scan and explicit device selection only required for boats with multiple ESL devices
|
|
17
|
+
## Templating
|
|
12
18
|
- Now support a directory of templates, where each is named like `416-240-BWRY.svg` to support same functions on different devices.
|
|
13
19
|
- Picks the template within the directory that most closely matches the tide clock height/width/colour-set, if not matched then height/width, and then best h/w ratio for nearest width
|
|
14
20
|
- `template/assets` is now `template/.assets`
|
package/README.md
CHANGED
|
@@ -72,12 +72,17 @@ npm install @rhizomatics/signalk-einklabel-plugin
|
|
|
72
72
|
|
|
73
73
|

|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
Template available as 416x240-BWRY for 3.7" ESLs and a simpler template, sized 250x128, also BWRY, for the cheapest 2.13" labels.
|
|
76
76
|
|
|
77
|
-
|
|
77
|
+
#### Pre-requisites
|
|
78
|
+
|
|
79
|
+
* [signalk-tides](https://github.com/openwatersio/signalk-tides) plugin to be installed and publishing tides to the Resources API.
|
|
80
|
+
- The [tides](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tides/) templates can be customized to run with other APIs or take data only from SignalK data paths.
|
|
81
|
+
- In the template it uses paths like `source=resources,resource=tides,provider=tides,path=extremes[0].time,format=local_time` to get the first tide time, ensures its the preferred `signalk-tides` provider and makes it a simple local time rather than a UTC date time.
|
|
82
|
+
* To show the lunar phase, the `environment.moon.phaseName` path is required, which can
|
|
78
83
|
be easily achieved by installing and configuring the `derived-data` plugin.
|
|
79
84
|
|
|
80
|
-
##
|
|
85
|
+
## Configuration
|
|
81
86
|
|
|
82
87
|
Use the standard configuration option in the SignalK menu for the plugin.
|
|
83
88
|
|
|
@@ -95,7 +100,7 @@ The plugin can optionally re-scan whenever it starts up (off by default), althou
|
|
|
95
100
|
|
|
96
101
|
A device's "Device" field can either be a specific device picked from the scan dropdown, or **"All discovered devices"**, which paints that same template/trigger to every device the plugin currently knows about. This is the simplest option for a boat with just one label - there's no need to scan first and pick it out, and if nothing's been discovered yet, selecting it triggers a scan itself the first time it's needed. It also covers several identical labels with one config entry, without listing each one out.
|
|
97
102
|
|
|
98
|
-
###
|
|
103
|
+
### Scheduling
|
|
99
104
|
|
|
100
105
|
There are two ways of scheduling scans:
|
|
101
106
|
|
|
@@ -323,6 +328,8 @@ For example, `npx esl-cli fields -t templates/tide.svg -e examples` will show al
|
|
|
323
328
|
|
|
324
329
|
SignalK plugins lack ability to self-update after something like a scan, so first time round you may have to close the config and reload it to see this. Subsequently the plugin will remember all scanned devices, and only drop previously seen ones if it goes 24 hours without a positive scan or with failed paint attempts.
|
|
325
330
|
|
|
331
|
+
Easiest way to solve this is to choose 'All Discovered Devices' in the device configuration, and it will paint any compatible devices it finds on future scans.
|
|
332
|
+
|
|
326
333
|
### Sometimes values are missing on the display
|
|
327
334
|
|
|
328
335
|
If the plugin repaints a display at server startup, then the plugin that provides the data may not have started ( or in the case of `derived-data` the plugin that the plugin depends on! ) and unlike Home Assistant, there's no good way of sequencing the start of plugins.
|
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;
|