@rhizomatics/signalk-einklabel-plugin 0.9.0-beta2 → 0.9.1

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,14 +1,33 @@
1
+ # 0.9.1
2
+
3
+ - Fix for upstream EventEmitter leak in `@naugehyde/node-ble`
4
+
1
5
  # 0.9.0
2
6
 
7
+ ## Tide Clock Example
8
+
3
9
  - Rename `third_quarter` to `last_quarter` for moon phase icons in `templates/.assets/lunar_phases` to match Derived Data plugin
4
10
  - `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
11
+ - Added a simpler _250x128_ version of tide clock for 2.13" labels
12
+
13
+ ## Fixes
14
+
6
15
  - Fix configuration JSON holding older versions of itself as subentries
7
- - Repaint state used to track which labels to repaint separately tracks the template and data changes
16
+ and data changes
8
17
  - Fix most cases of scanned devices not appearing in device dropdown choice
18
+
19
+ ## Improvements
20
+
21
+ - Repaint state used to track which labels to repaint separately tracks the template
9
22
  - `nearestColour` algorithm in Zhsunyco driver supports ESLs that only have BWR or BW
10
- - New 'ALL' as device option, and by default disable initial scan, to optimize support for single devices
11
- - Scan and explicit device selection only required then for boats with multiple devices
23
+
24
+ ## Device Selection
25
+
26
+ - 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
27
+ - Scan and explicit device selection only required for boats with multiple ESL devices
28
+
29
+ ## Templating
30
+
12
31
  - Now support a directory of templates, where each is named like `416-240-BWRY.svg` to support same functions on different devices.
13
32
  - 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
33
  - `template/assets` is now `template/.assets`
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  Fully working but limited vendor/product support and requires Linux for device access.
11
11
 
12
- A SignalK plugin to display data from SignalK paths, APIs and plugins on Electronic Shelf Labels (ESL) over a Bluetooth Low Energy (BLE) connection using simple SVG templates.
12
+ A SignalK plugin to display data from SignalK paths, Resource APIs and plugins on Electronic Shelf Labels (ESL) over a Bluetooth Low Energy (BLE) connection using simple SVG templates.
13
13
 
14
14
  ## What is an ESL?
15
15
 
@@ -23,18 +23,18 @@ Being battery operated, they can be stuck on anywhere without wiring - the only
23
23
 
24
24
  Unlike some eInk projects, this plugin doesn't require any physical modification to the labels, or loading any new firmware. It can send an image to a supported shelf label fresh out of the box.
25
25
 
26
- Most of these requirements are about making SignalK work with Bluetooth Low Energy, which is good thing to have anyway, since vendors like Victron, Switchbot, Ruuvi and others have BLE enabled hardware that's useful to have on a boat. [Direct BLE support](https://github.com/SignalK/signalk-server/issues/2411) in SignalK is being planned in 2026.
26
+ Most of requirements below are to make SignalK work with Bluetooth Low Energy, which is good thing to have anyway, since vendors like Victron, Switchbot, Ruuvi and others have BLE enabled hardware that's useful to have on a boat. [Direct BLE support](https://github.com/SignalK/signalk-server/issues/2411) in SignalK is being planned in 2026 and this plugin will support that when it comes.
27
27
 
28
28
  1. A SignalK server, **running Linux**
29
29
 
30
30
  - MacOS and Windows aren't supported by the [BLE interface layer](https://www.npmjs.com/package/@naugehyde/node-ble), however can be used for template development and
31
31
  debugging (everything except `scan` and `paint`)
32
32
 
33
- 2. A Bluetooth adapter, that can handle BLE (Bluetooth Low Energy), which is Bluetooth v4.0 or higher
33
+ 2. A Bluetooth adapter, that can handle BLE (Bluetooth Low Energy).
34
34
 
35
35
  - Bluetooth adapters for Linux can be tricky, TP-Link UB400 and Asus USB-BT500 are two well-known and available ones
36
36
  - Some Raspberry Pi models come with suitable Bluetooth it built-in
37
- - Don't worry about the very latest Bluetooth versions, 4.0 is basic, 5.0 is nice
37
+ - Don't worry about the very latest Bluetooth versions, 4.0 is minimum for BLE, 5.0 is nice
38
38
  - Home Assistant is massively more popular than SignalK, and often also run on Raspberry Pi and similar, so good source of advice
39
39
 
40
40
  3. `bluez` package installed in Linux
@@ -44,18 +44,18 @@ Most of these requirements are about making SignalK work with Bluetooth Low Ener
44
44
 
45
45
  4. One or more supported Electronic Shelf Labels
46
46
 
47
- - The label used for testing this is the [ZhunyCo 3.7 BRWY](https://www.aliexpress.com/item/1005010050104435.html)
47
+ - The label used for testing this is the [ZhunyCo 3.7" BRWY](https://www.aliexpress.com/item/1005010050104435.html)
48
48
 
49
49
  5. Correct time zone set on server if local time is to be shown on display
50
50
 
51
51
  - See [FAQ](#faq-timezone)
52
52
  - If not set, everything will work, but you may see the wrong zone or not have daylight savings applied
53
53
 
54
- Once you have all of that, it may be worth also installing [signalk-victron-ble](https://github.com/stefanor/signalk-victron-ble) or [bt-sensors-plugin](https://github.com/naugehyde/bt-sensors-plugin-sk) to pull in data from other sensors and equipment.
54
+ Once you have all of that, it may be worth also installing [signalk-victron-ble](https://github.com/stefanor/signalk-victron-ble),[signalk-ruuvitag-plugin](https://github.com/vokkim/signalk-ruuvitag-plugin) or [bt-sensors-plugin](https://github.com/naugehyde/bt-sensors-plugin-sk) to pull in data from other sensors and equipment.
55
55
 
56
56
  ## Installation
57
57
 
58
- Look for **eInk Label Instrument** in the [SignalK AppStore]() on your
58
+ Look for **eInk Label Displays** in the **SignalK AppStore** on your
59
59
  server ( under _Apps & Plugins_ on the latest version).
60
60
 
61
61
  ### Using Outside of SignalK
@@ -66,22 +66,53 @@ The plugin can also be installed as a stand-alone module, which can be useful fo
66
66
  npm install @rhizomatics/signalk-einklabel-plugin
67
67
  ```
68
68
 
69
+ ## Configuration
70
+
71
+ Use the standard configuration option in the SignalK menu for the plugin.
72
+
73
+ ![Plugin Configuration](docs/assets/screenshots/plugin_config.png)
74
+
69
75
  ## Examples
70
76
 
71
77
  ### Tide Clock
72
78
 
73
79
  ![Tide Clock](docs/assets/screenshots/example_tidal_clock.png)
74
80
 
75
- The tide clock needs the [signalk-tides](https://github.com/openwatersio/signalk-tides) plugin to be installed and publishing tides to the Resources API. The [tide.svg](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tide.svg) can be customized to run with other APIs or take data only from SignalK data paths. For example, `source=resources,resource=tides,provider=tides,path=extremes[0].time,format=local_time` gets the first tide time, ensures its the preferred `signalk-tides` provider and makes it a simple local time rather than a UTC date time.
81
+ Template available as 416x240-BWRY for 3.7" ESLs and a simpler template, sized 250x128, also BWRY, for the cheapest 2.13" labels.
82
+
83
+ #### Pre-requisites
84
+
85
+ A _tides_ provider plugin for the Resources API installed and enabled, currently one of:
86
+
87
+ - [signalk-tides](https://github.com/openwatersio/signalk-tides) - uses [neaps](https://github.com/openwatersio/neaps) library for international off-line coverage
88
+ - [signalk-mareas-ihm](https://github.com/Aitonos/signalk-mareas-ihm) - interfaces with official Spanish IHM tidal predictions, or falls back to Open Meteo and _signalk-tides_
89
+
90
+ The [tides](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tides/) templates can be customized to run with any tide provider, a specific one, or switch to other APIs or SignalK data paths.
91
+
92
+ - In the template it uses a SVG description 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.
76
93
 
77
94
  To show the lunar phase, the `environment.moon.phaseName` path is required, which can
78
95
  be easily achieved by installing and configuring the `derived-data` plugin.
79
96
 
80
- ## Configuration
97
+ ## Setting up a Label
81
98
 
82
- Use the standard configuration option in the SignalK menu for the plugin.
99
+ Enable the plugin, and use the large **+** sign to add a label, which open up these fields.
83
100
 
84
- ![Plugin Configuration](docs/assets/screenshots/plugin_config.png)
101
+ ![Label Config](docs/assets/screenshots/label_config.png)
102
+
103
+ - _Friendly Name_ - Give the label any name (word or phrase) you like, for example 'Tide Clock'
104
+ - _Device_ - Unless you have multiple labels, don't bother with pre-scanning or selecting a specifig device, instead pick **"All discovered devices"** and it will paint any compatible labels it finds. If you want to pick a specific device, you'll need to wait for a device scan to complete.
105
+ - _Template_ - Choose a built in template, or one you've added to the local templates directory
106
+ - _Repaint Trigger_- Do you want this to repaint every few hours (at a chosen minutes past hour), or when a SignalK path changes?
107
+ - If its a SignalK path, enter it next, for example `environment.tide.state`
108
+ - If its time based, enter how many hours between repaints, for example 00:00/08:00/16:00 for an 8h schedule, and if you want a specific number of minutes after the hour.
109
+
110
+ There are also two more advanced options, which can usually be ignored.
111
+
112
+ - _BLE AES key_ - Only needed if the default key doesn't work and you have a better alternative, otherwise ignore
113
+ - _Force Repaint_ - Next time the label is due to be painted, update even if the data or template hasn't changed (this flag will automatically be cleared after this.)
114
+
115
+ When the plugin starts, it will automatically re-paint the label if its new, or the last timed slot was missed and the data has changed.
85
116
 
86
117
  ### Scanning for Devices
87
118
 
@@ -91,21 +122,18 @@ One other quirk is that some devices respond with a different name at different
91
122
 
92
123
  The plugin can optionally re-scan whenever it starts up (off by default), although this isn't essential once a label has been configured. Devices found by any scan are remembered across restarts - see the FAQ below.
93
124
 
94
- #### Selecting a Device
95
-
96
- 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
-
98
- ### Scheduling
99
-
100
- There are two ways of scheduling scans:
125
+ ## Vendors
101
126
 
102
- #### Time Based
127
+ ### Zhsunyco
103
128
 
104
- This will schedule at fixed hours of the day, for example 00:00/08:00/16:00 for an 8h schedule, and at a selected minutes past the hour. At plugin startup/restart, the devices will be repainted if they missed their last slot.
129
+ Also known as 'Suny' and 'WOLink'.
105
130
 
106
- #### Path Subscription
131
+ - [BLE ESLs](https://www.zhsunyco.com/digital-display-solution-for-small-retail-business/ble-esl-solution/)
132
+ - The range of labels available on retail sites like AliExpress may be larger than on their corporate site
133
+ - In mid 2026, a 4 colour (BWRY) 3.7" label retailed for about $35, with quantity discounts for bulk sets
134
+ - Cheapest units are 2 colour 1.54", and they go up to 7.5"
107
135
 
108
- The devices will be painted when the plugin starts, and then every time the selected SignalK path changes.
136
+ Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
109
137
 
110
138
  ## Templating
111
139
 
@@ -113,7 +141,9 @@ Templates are simply SVG files, to which expressions can be added to use SignalK
113
141
 
114
142
  ### Template Families (multiple panel sizes/colours)
115
143
 
116
- A "Template" selection can either be one specific `.svg` file, or a _directory_ holding several same-purpose templates for different panel sizes/colour-sets, e.g. `templates/tides/416x240-BWRY.svg` and `templates/tides/250x128-BWRY.svg` both implement the tide clock, just at different sizes. Each file is named `<width>x<height>-<colours>.svg`, where `<colours>` is one letter per supported colour: `B`(lack)/`W`(hite)/`R`(ed)/`Y`(ellow) - e.g. `BWRY` for a 4-colour panel, `BWR` for a 3-colour one.
144
+ A "Template" selection can either be one specific `.svg` file, or a _directory_ holding several versions of the same template for different panel sizes/colour-sets, e.g. `templates/tides/416x240-BWRY.svg` and `templates/tides/250x128-BWRY.svg` both implement the tide clock, just at different sizes.
145
+
146
+ Each template is named `<width>x<height>-<colours>.svg`, where `<colours>` is one letter per supported colour: `B`(lack)/`W`(hite)/`R`(ed)/`Y`(ellow) - e.g. `BWRY` for a 4-colour panel, `BWR` for a 3-colour one.
117
147
 
118
148
  Selecting the directory (e.g. `tides`) instead of one file lets one `DeviceConfig` entry - especially a `device: "All discovered devices"` entry covering several different physical panels - automatically pick the best-fitting file for each device's actual size/colours, trying in order:
119
149
 
@@ -135,7 +165,7 @@ The source can be overridden to use the SignalK server's Resources API instead.
135
165
 
136
166
  For example, `source=resources,resource=tides,provider=tides,path=station.name` picks the `tides` resource and pulls the `station.name` path out of the JSON response - this works for any resource type (`tides`, `waypoints`, `routes`, ...), and needs nothing configured: the plugin reaches the Resources API directly. Where a resource is specified, it will be fetched once for that render, and subsequent fields sourced from the same resource use that cached response. `provider` is optional, the default provider will be used if not specified.
137
167
 
138
- #### Plugin Data
168
+ #### Plugin Derived Data
139
169
 
140
170
  `source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Available paths:
141
171
 
@@ -158,7 +188,7 @@ SignalK's unit preferences are used to automatically convert a `signalk`-sourced
158
188
 
159
189
  Note that for dates and times, the server timezone must be set correctly, for example `Europe/London` rather than the default `Etc/UTC`. This can be done on Linux using `timedatectl` or `raspi-config` if using a Raspberry Pi with Raspian.
160
190
 
161
- Common categories:
191
+ ##### Common categories
162
192
 
163
193
  - `depth` - Use the SignalK preferred depth unit, make the conversion if needed, and tack on the unit name as a suffix
164
194
  - `speed` - Use the SignalK preferred speed unit, make the conversion if needed, and tack on the unit name as a suffix
@@ -178,7 +208,7 @@ path=environment.moon.phaseName,assets=lunar_phases
178
208
 
179
209
  which resolves against `templates/.assets/lunar_phases/` (bundled, or your own configured `templates` directory's `.assets/lunar_phases/` if you have one). The resolved value (e.g. `"Waning Gibbous"`, as published by the [derived-data](https://www.npmjs.com/package/signalk-derived-data) plugin) is normalized to match a filename - lower-cased, punctuation and spaces collapsed to underscores - so `"Waning Gibbous"` picks `waning_gibbous.svg` out of that directory. If the underlying path has no value at all (e.g. the `derived-data` plugin isn't installed), or the value doesn't normalize to any file in the directory, the `<image>` element is omitted from that render - no broken image, no placeholder, nothing shown - and a line is logged to the console so a missing/unmatched value isn't silently invisible.
180
210
 
181
- If you don't like the bundled moon phase icons, save your own `<value>.svg` files in the `.assets/lunar_phases` sub-directory of your configured `templates` directory - the whole directory is used in place of the bundled one, so add all 8 phases you want to keep, not just the ones you're changing.
211
+ If you don't like the bundled moon phase icons, save your own `<value>.svg` files in the `.assets/lunar_phases` sub-directory of your configured `templates` directory - the whole directory is used in place of the bundled one, so add all 8 phases you want to keep, not just the ones you're changing. These moon phases can be re-used in any other label.
182
212
 
183
213
  This is a general mechanism, not specific to moon phases - any `source`/`context`/`path`/`format` combination valid for a `<text>` binding works here too (a `source=resources` value, an explicit `category=`, etc.), the only difference is the required `assets=` directory and the "no match -> no image" behaviour instead of substituted text. To add your own, put a directory of `<value>.svg` files under an `.assets/<name>` sub-directory of your `templates` directory, add an `<image>` element in your SVG editor at the size/position you want, and give it a `<desc>` the same way you would a text field - overriding just the template, just its assets, or both together, all work independently.
184
214
 
@@ -190,19 +220,6 @@ Three font types are loaded by default, use the generic font family, or exact fo
190
220
  - `sans-serif` - `Roboto`
191
221
  - `monospace` - `Roboto Mono`
192
222
 
193
- ## Vendors
194
-
195
- ### Zhsunyco
196
-
197
- Also known as 'Suny' and 'WOLink'.
198
-
199
- - [BLE ESLs](https://www.zhsunyco.com/digital-display-solution-for-small-retail-business/ble-esl-solution/)
200
- - The range of labels available on retail sites like AliExpress may be larger than on their corporate site
201
- - In mid 2026, a 4 colour (BWRY) 3.7" label retailed for about $35, with quantity discounts for bulk sets
202
- - Cheapest units are 2 colour 1.54", and they go up to 7.5"
203
-
204
- Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
205
-
206
223
  ## Architecture
207
224
 
208
225
  The primary things managed and provided by the plugin are:
@@ -266,7 +283,9 @@ Placeholder text isn't necessary, and is ignored by the plugin, but makes it muc
266
283
 
267
284
  Inkscape adds its own metadata to images, which can be stripped off by exporting a simple SVG, although can be left in place with no harm; main reason to simplify the SVG is manual changes in a text editor.
268
285
 
269
- Due to a limitation in the `resvg-wasm` library used to turn SVGs into images, the `font-family` is limited to `serif`,`sans-serif`,`monospace` or the exact name of one of the installed fonts - `Roboto` (sans serif), `Roboto Serif` or `Roboto Mono`. Inkscape has its own fonts, which won't match what's available in the SignalK plugin, so for more precise design, install [Roboto from Google](https://fonts.google.com/specimen/Roboto) via the web page, `brew` on MacOS or similar.
286
+ Due to a limitation in the `resvg-wasm` library used to turn SVGs into images, the `font-family` is limited to `serif`,`sans-serif`,`monospace` or the exact name of one of the installed fonts - `Roboto` (sans serif), `Roboto Serif` or `Roboto Mono`.
287
+
288
+ Inkscape has its own fonts, which won't match what's available in the SignalK plugin, so for more precise design, install [Roboto from Google](https://fonts.google.com/specimen/Roboto) via the web page, `brew` on MacOS or similar.
270
289
 
271
290
  ### Debugging Templates
272
291
 
@@ -323,6 +342,8 @@ For example, `npx esl-cli fields -t templates/tide.svg -e examples` will show al
323
342
 
324
343
  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
344
 
345
+ 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.
346
+
326
347
  ### Sometimes values are missing on the display
327
348
 
328
349
  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.
@@ -102,6 +102,20 @@ async function withDiscovery(durationMs, fn) {
102
102
  destroy();
103
103
  }
104
104
  }
105
+ /**
106
+ * node-ble's `Device#connect()` adds a fresh D-Bus `PropertiesChanged` listener on every call
107
+ * and only removes it via a *successful* `Device#disconnect()` - a connect that times out or
108
+ * rejects (exactly the case this function exists to bound, for a device that's out of range or
109
+ * stuck mid-handshake) never reaches that cleanup, so each failed attempt leaks one listener on
110
+ * the underlying Device object. A flaky device polled on every scan/repaint cycle eventually
111
+ * trips Node's MaxListenersExceededWarning. Reach into node-ble's internal BusHelper (not part
112
+ * of its public API, but the only thing that actually holds the listener) and clear it directly
113
+ * regardless of how the attempt ended, so at most one listener is ever outstanding.
114
+ * (github.com/naugehyde/node-ble — fix proposed upstream, not yet released.)
115
+ */
116
+ function clearStaleConnectListener(device) {
117
+ device.helper?.removeAllListeners?.("PropertiesChanged");
118
+ }
105
119
  /**
106
120
  * `device.connect()` has no timeout of its own - BlueZ's underlying D-Bus `Connect` call can hang
107
121
  * indefinitely for a device that's out of range or stuck mid-handshake, which would otherwise
@@ -112,7 +126,12 @@ async function withDiscovery(durationMs, fn) {
112
126
  async function connectWithTimeout(device, timeoutMs) {
113
127
  const connecting = device.connect();
114
128
  let timedOut = false;
115
- await Promise.race([connecting, sleep(timeoutMs).then(() => void (timedOut = true))]);
129
+ try {
130
+ await Promise.race([connecting, sleep(timeoutMs).then(() => void (timedOut = true))]);
131
+ }
132
+ finally {
133
+ clearStaleConnectListener(device);
134
+ }
116
135
  if (timedOut) {
117
136
  connecting.then(() => device.disconnect()).catch(() => { });
118
137
  throw new Error(`connecting to device timed out after ${timeoutMs}ms`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rhizomatics/signalk-einklabel-plugin",
3
- "version": "0.9.0-beta2",
3
+ "version": "0.9.1",
4
4
  "description": "Display SignalK data on eInk Electronic Shelf Labels",
5
5
  "keywords": [
6
6
  "ble",