@rhizomatics/signalk-einklabel-plugin 0.8.1 → 0.8.2

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 CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.8.2
2
+
3
+ - Work around a SignalK bug in Server API where `ResourcesApi.listResources()` merges provider values in undetermined order and ignores specified provider.
4
+
1
5
  # 0.8.1
2
6
 
3
7
  - A preferred `provider` can now be set for `resources`.
package/README.md CHANGED
@@ -322,6 +322,10 @@ If you're a global cruiser, then use something like [signalk-set-gps-timezone](h
322
322
 
323
323
  Try a BLE proxy device, ESP32 is popular for this.
324
324
 
325
+ ### Can't edit the text contents of SVG template in VSCode
326
+
327
+ If you have an SVG viewer extension, this wll show the image rather than allowing editing of text. To solve, right click on the file in VSCode _Explorer_ view and choose to edit with _Text Editor_.
328
+
325
329
  ## Other ESL and General eInk Resources
326
330
 
327
331
  - [Open ePaper Link](https://openepaperlink.de) - Alternative open source firmware to flash onto eInk shelf labels, with Home Assistant integration.
@@ -150,7 +150,7 @@ async function readDeviceDetails(device, address, advertisedInfo) {
150
150
  // which otherwise stays set forever and silently skips every later scan).
151
151
  return Promise.race([
152
152
  read(),
153
- (0, bleDiscovery_1.sleep)(SCAN_CONNECT_TIMEOUT_MS * 2).then(() => {
153
+ (0, bleDiscovery_1.sleep)(SCAN_CONNECT_TIMEOUT_MS * 3).then(() => {
154
154
  console.error(`${pluginVersion_1.PLUGIN_NAME}: zhsunyco [${address}]: battery/config read timed out after ${SCAN_CONNECT_TIMEOUT_MS * 2}ms`);
155
155
  return fallback;
156
156
  }),
@@ -21,3 +21,9 @@ export declare function resolveAssetPath(templatesDir: string, bundledTemplatesD
21
21
  * directory, is readable) - the miss is then just this value's.
22
22
  */
23
23
  export declare function describeAssetsDirProblem(templatesDir: string, bundledTemplatesDir: string, assetsName: string): string | undefined;
24
+ /**
25
+ * Counts the `.svg` files in whichever `assets/<assetsName>` directory `selectAssetsDir` would read
26
+ * from, for a caller to report alongside a resolved-but-empty-key miss - confirms which directory is in
27
+ * play and how populated it is, which "the directory exists" alone doesn't convey.
28
+ */
29
+ export declare function countAssets(templatesDir: string, bundledTemplatesDir: string, assetsName: string): number;
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.normalizeAssetKey = normalizeAssetKey;
4
4
  exports.resolveAssetPath = resolveAssetPath;
5
5
  exports.describeAssetsDirProblem = describeAssetsDirProblem;
6
+ exports.countAssets = countAssets;
6
7
  const fs_1 = require("fs");
7
8
  const path_1 = require("path");
8
9
  /**
@@ -86,3 +87,16 @@ function describeAssetsDirProblem(templatesDir, bundledTemplatesDir, assetsName)
86
87
  const problem = describeDir(selectAssetsDir(templatesDir, bundledTemplatesDir, assetsName));
87
88
  return problem && `assets directory ${problem}`;
88
89
  }
90
+ /**
91
+ * Counts the `.svg` files in whichever `assets/<assetsName>` directory `selectAssetsDir` would read
92
+ * from, for a caller to report alongside a resolved-but-empty-key miss - confirms which directory is in
93
+ * play and how populated it is, which "the directory exists" alone doesn't convey.
94
+ */
95
+ function countAssets(templatesDir, bundledTemplatesDir, assetsName) {
96
+ try {
97
+ return (0, fs_1.readdirSync)(selectAssetsDir(templatesDir, bundledTemplatesDir, assetsName)).filter((name) => name.endsWith(".svg")).length;
98
+ }
99
+ catch {
100
+ return 0;
101
+ }
102
+ }
@@ -136,7 +136,7 @@ class SvgRenderer {
136
136
  console.error(key
137
137
  ? `${pluginVersion_1.PLUGIN_NAME}: image "${descElement.textContent}" has no asset file for value "${key}" in "${binding.assets}"`
138
138
  : `${pluginVersion_1.PLUGIN_NAME}: image "${descElement.textContent}" resolved to empty value to select asset`);
139
- console.error(`${pluginVersion_1.PLUGIN_NAME}: ${dirProblem ?? `assets directory for "${binding.assets}" is present`}`);
139
+ console.error(`${pluginVersion_1.PLUGIN_NAME}: ${dirProblem ?? `directory "${binding.assets}" found with ${(0, assets_1.countAssets)(templatesDir, bundledTemplatesDir, binding.assets)} assets`}`);
140
140
  element.parentNode?.removeChild(element);
141
141
  continue;
142
142
  }
@@ -16,8 +16,10 @@ const unitCategories_1 = require("./unitCategories");
16
16
  const pathMeta_1 = require("./pathMeta");
17
17
  const resolveApiUrl_1 = require("./resolveApiUrl");
18
18
  const pluginVersion_1 = require("./pluginVersion");
19
+ const httpJson_1 = require("./httpJson");
19
20
  const INTERVAL_POLL_MS = 60000;
20
21
  const SUBSCRIPTION_DEBOUNCE_MS = 2000;
22
+ const RESOURCES_API_PATH = "/signalk/v2/api/resources";
21
23
  /**
22
24
  * The most recent wall-clock instant at or before `now` that an `interval`-triggered device's own
23
25
  * periodic check (below, in `startRepaintScheduler`) would fire at - every `intervalHours` hours,
@@ -85,13 +87,16 @@ function setAtPath(target, path, value) {
85
87
  * CLI's HTTP-fetched equivalent (`assembleLiveContext` in `cli/liveContext.ts`), so a `path=` binding
86
88
  * resolves to the same value either way - without it, `resolveBinding` would hand back the whole
87
89
  * wrapper object instead of e.g. a plain string, breaking any binding that expects one (a `<image>`'s
88
- * `assets=`, a `format=` formatter, etc). `resources`-sourced bindings go through `app.resourcesApi`,
89
- * in-process too, with no such wrapper. Per-path unit-conversion metadata (`pathMeta`, for automatic
90
- * conversion - see `renderBinding`) and an explicit `category=` binding's resolved conversion both
91
- * have no in-process equivalent (confirmed against the signalk-server source - that resolution only
92
- * happens in its REST layer), so both need `apiUrl` - fetching `pathMeta` is best-effort (a
93
- * missing/unreachable server just means no automatic conversion), but a `category=` binding is a
94
- * declared dependency, so a missing `apiUrl` is a hard error there.
90
+ * `assets=`, a `format=` formatter, etc). `resources`-sourced bindings with no `provider=` pin go
91
+ * through `app.resourcesApi` in-process too, with no such wrapper - but a `provider=`-pinned binding
92
+ * goes over HTTP instead (see the resources loop below), since the in-process API doesn't actually
93
+ * honor `providerId` on signalk-server 2.30.0. Per-path unit-conversion metadata (`pathMeta`, for
94
+ * automatic conversion - see `renderBinding`) and an explicit `category=` binding's resolved
95
+ * conversion both have no in-process equivalent (confirmed against the signalk-server source - that
96
+ * resolution only happens in its REST layer), so both need `apiUrl` - fetching `pathMeta` is
97
+ * best-effort (a missing/unreachable server just means no automatic conversion), but a `category=`
98
+ * binding (and now a `provider=`-pinned resource binding) is a declared dependency, so a missing
99
+ * `apiUrl` is a hard error there.
95
100
  */
96
101
  async function assembleRawContext(app, apiUrl, bindings) {
97
102
  var _a;
@@ -130,13 +135,26 @@ async function assembleRawContext(app, apiUrl, bindings) {
130
135
  resourceBindings.set((0, binding_1.resourceContextKey)(binding), binding);
131
136
  }
132
137
  for (const [key, binding] of resourceBindings) {
133
- // `listResources`'s type only allows the standard SignalKResourceType union, but the underlying
134
- // Resources API (and a custom provider like signalk-tides, registered under the non-standard
135
- // "tides" type) accepts any registered resource type string - this cast matches `getResource`'s
136
- // wider, accurate signature. An explicit `provider=` pins which registered provider plugin
137
- // answers the request (SignalK's `listResources(resType, params, providerId)`) rather than
138
- // leaving it to whichever provider the server considers the default for that resource type.
139
- resources[key] = await app.resourcesApi.listResources(binding.resource, {}, binding.provider);
138
+ if (binding.provider) {
139
+ // `app.resourcesApi.listResources(resType, params, providerId)` validates providerId against the
140
+ // registered providers but then - confirmed against signalk-server 2.30.0's own source - ignores
141
+ // it and merges every registered provider's results together (its internal `listFromAll`), so a
142
+ // second provider for the same resource type can silently clobber the one a binding pinned. The
143
+ // REST API's own `?provider=` query param doesn't have this bug (it queries only that provider),
144
+ // so a pinned binding goes over HTTP instead of the in-process API to actually get what it asked for.
145
+ if (!apiUrl) {
146
+ throw new Error(`binding pins resource "${binding.resource}" to provider "${binding.provider}" but no SignalK API base URL is configured`);
147
+ }
148
+ resources[key] = await (0, httpJson_1.fetchJson)(`${apiUrl}${RESOURCES_API_PATH}/${binding.resource}?provider=${encodeURIComponent(binding.provider)}`);
149
+ }
150
+ else {
151
+ // `listResources`'s type only allows the standard SignalKResourceType union, but the underlying
152
+ // Resources API (and a custom provider like signalk-tides, registered under the non-standard
153
+ // "tides" type) accepts any registered resource type string - this cast matches `getResource`'s
154
+ // wider, accurate signature. No `provider=` pin here, so the in-process call's default-provider
155
+ // behavior (whichever the server picks) is fine - and cheaper than a redundant HTTP round trip.
156
+ resources[key] = await app.resourcesApi.listResources(binding.resource, {}, undefined);
157
+ }
140
158
  }
141
159
  const categoryNames = new Set(bindings.filter((binding) => binding.category).map((binding) => binding.category));
142
160
  if (categoryNames.size > 0 && !apiUrl) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizomatics/signalk-einklabel-plugin",
3
- "version": "0.8.1",
3
+ "version": "0.8.2",
4
4
  "description": "Display SignalK data on eInk Electronic Shelf Labels",
5
5
  "keywords": [
6
6
  "ble",
@@ -12,7 +12,10 @@
12
12
  xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
13
13
  xmlns:xlink="http://www.w3.org/1999/xlink"
14
14
  xmlns="http://www.w3.org/2000/svg"
15
- xmlns:svg="http://www.w3.org/2000/svg"><defs
15
+ xmlns:svg="http://www.w3.org/2000/svg"
16
+ xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
17
+ xmlns:cc="http://creativecommons.org/ns#"
18
+ xmlns:dc="http://purl.org/dc/elements/1.1/"><defs
16
19
  id="defs1" /><sodipodi:namedview
17
20
  id="namedview1"
18
21
  pagecolor="#ffffff"