@rhizomatics/signalk-einklabel-plugin 0.9.0 → 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 +16 -3
- package/README.md +57 -43
- package/dist/devices/bleDiscovery.js +20 -1
- package/docs/assets/screenshots/label_config.png +0 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +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
|
|
|
3
7
|
## Tide Clock Example
|
|
8
|
+
|
|
4
9
|
- Rename `third_quarter` to `last_quarter` for moon phase icons in `templates/.assets/lunar_phases` to match Derived Data plugin
|
|
5
10
|
- `tide.svg` renamed to `tides\416x240-BWRY.svg`. Original maintained but labelled as deprecated
|
|
6
|
-
- Added a simpler
|
|
11
|
+
- Added a simpler _250x128_ version of tide clock for 2.13" labels
|
|
12
|
+
|
|
7
13
|
## Fixes
|
|
14
|
+
|
|
8
15
|
- Fix configuration JSON holding older versions of itself as subentries
|
|
9
|
-
and data changes
|
|
16
|
+
and data changes
|
|
10
17
|
- Fix most cases of scanned devices not appearing in device dropdown choice
|
|
18
|
+
|
|
11
19
|
## Improvements
|
|
12
|
-
|
|
20
|
+
|
|
21
|
+
- Repaint state used to track which labels to repaint separately tracks the template
|
|
13
22
|
- `nearestColour` algorithm in Zhsunyco driver supports ESLs that only have BWR or BW
|
|
23
|
+
|
|
14
24
|
## Device Selection
|
|
25
|
+
|
|
15
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
|
|
16
27
|
- Scan and explicit device selection only required for boats with multiple ESL devices
|
|
28
|
+
|
|
17
29
|
## Templating
|
|
30
|
+
|
|
18
31
|
- Now support a directory of templates, where each is named like `416-240-BWRY.svg` to support same functions on different devices.
|
|
19
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
|
|
20
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
|
|
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)
|
|
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
|
|
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
|
|
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,6 +66,12 @@ 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
|
+

|
|
74
|
+
|
|
69
75
|
## Examples
|
|
70
76
|
|
|
71
77
|
### Tide Clock
|
|
@@ -76,17 +82,37 @@ Template available as 416x240-BWRY for 3.7" ESLs and a simpler template, sized 2
|
|
|
76
82
|
|
|
77
83
|
#### Pre-requisites
|
|
78
84
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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.
|
|
93
|
+
|
|
94
|
+
To show the lunar phase, the `environment.moon.phaseName` path is required, which can
|
|
83
95
|
be easily achieved by installing and configuring the `derived-data` plugin.
|
|
84
96
|
|
|
85
|
-
##
|
|
97
|
+
## Setting up a Label
|
|
86
98
|
|
|
87
|
-
|
|
99
|
+
Enable the plugin, and use the large **+** sign to add a label, which open up these fields.
|
|
88
100
|
|
|
89
|
-

|
|
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.
|
|
90
116
|
|
|
91
117
|
### Scanning for Devices
|
|
92
118
|
|
|
@@ -96,21 +122,18 @@ One other quirk is that some devices respond with a different name at different
|
|
|
96
122
|
|
|
97
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.
|
|
98
124
|
|
|
99
|
-
|
|
100
|
-
|
|
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.
|
|
102
|
-
|
|
103
|
-
### Scheduling
|
|
104
|
-
|
|
105
|
-
There are two ways of scheduling scans:
|
|
125
|
+
## Vendors
|
|
106
126
|
|
|
107
|
-
|
|
127
|
+
### Zhsunyco
|
|
108
128
|
|
|
109
|
-
|
|
129
|
+
Also known as 'Suny' and 'WOLink'.
|
|
110
130
|
|
|
111
|
-
|
|
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"
|
|
112
135
|
|
|
113
|
-
|
|
136
|
+
Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
|
|
114
137
|
|
|
115
138
|
## Templating
|
|
116
139
|
|
|
@@ -118,7 +141,9 @@ Templates are simply SVG files, to which expressions can be added to use SignalK
|
|
|
118
141
|
|
|
119
142
|
### Template Families (multiple panel sizes/colours)
|
|
120
143
|
|
|
121
|
-
A "Template" selection can either be one specific `.svg` file, or a _directory_ holding several same
|
|
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.
|
|
122
147
|
|
|
123
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:
|
|
124
149
|
|
|
@@ -140,7 +165,7 @@ The source can be overridden to use the SignalK server's Resources API instead.
|
|
|
140
165
|
|
|
141
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.
|
|
142
167
|
|
|
143
|
-
#### Plugin Data
|
|
168
|
+
#### Plugin Derived Data
|
|
144
169
|
|
|
145
170
|
`source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Available paths:
|
|
146
171
|
|
|
@@ -163,7 +188,7 @@ SignalK's unit preferences are used to automatically convert a `signalk`-sourced
|
|
|
163
188
|
|
|
164
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.
|
|
165
190
|
|
|
166
|
-
Common categories
|
|
191
|
+
##### Common categories
|
|
167
192
|
|
|
168
193
|
- `depth` - Use the SignalK preferred depth unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
169
194
|
- `speed` - Use the SignalK preferred speed unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
@@ -183,7 +208,7 @@ path=environment.moon.phaseName,assets=lunar_phases
|
|
|
183
208
|
|
|
184
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.
|
|
185
210
|
|
|
186
|
-
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.
|
|
187
212
|
|
|
188
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.
|
|
189
214
|
|
|
@@ -195,19 +220,6 @@ Three font types are loaded by default, use the generic font family, or exact fo
|
|
|
195
220
|
- `sans-serif` - `Roboto`
|
|
196
221
|
- `monospace` - `Roboto Mono`
|
|
197
222
|
|
|
198
|
-
## Vendors
|
|
199
|
-
|
|
200
|
-
### Zhsunyco
|
|
201
|
-
|
|
202
|
-
Also known as 'Suny' and 'WOLink'.
|
|
203
|
-
|
|
204
|
-
- [BLE ESLs](https://www.zhsunyco.com/digital-display-solution-for-small-retail-business/ble-esl-solution/)
|
|
205
|
-
- The range of labels available on retail sites like AliExpress may be larger than on their corporate site
|
|
206
|
-
- In mid 2026, a 4 colour (BWRY) 3.7" label retailed for about $35, with quantity discounts for bulk sets
|
|
207
|
-
- Cheapest units are 2 colour 1.54", and they go up to 7.5"
|
|
208
|
-
|
|
209
|
-
Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
|
|
210
|
-
|
|
211
223
|
## Architecture
|
|
212
224
|
|
|
213
225
|
The primary things managed and provided by the plugin are:
|
|
@@ -271,7 +283,9 @@ Placeholder text isn't necessary, and is ignored by the plugin, but makes it muc
|
|
|
271
283
|
|
|
272
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.
|
|
273
285
|
|
|
274
|
-
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`.
|
|
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.
|
|
275
289
|
|
|
276
290
|
### Debugging Templates
|
|
277
291
|
|
|
@@ -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
|
-
|
|
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`);
|
|
Binary file
|