@rhizomatics/signalk-einklabel-plugin 1.3.0-beta9 → 1.3.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 +28 -1
- package/README.md +14 -538
- package/dist/cli/index.js +1 -0
- package/dist/config.d.ts +53 -18
- package/dist/config.js +252 -85
- package/dist/devices/bleBackend.js +44 -7
- package/dist/devices/bleDiscovery.d.ts +12 -1
- package/dist/devices/bleDiscovery.js +27 -2
- package/dist/devices/gicisky/index.js +5 -0
- package/dist/devices/types.d.ts +11 -0
- package/dist/devices/zhsunyco/index.js +35 -6
- package/dist/docs/templateReference.d.ts +18 -0
- package/dist/docs/templateReference.js +207 -0
- package/dist/plugin.js +25 -12
- package/dist/render/assets.js +2 -1
- package/dist/render/templateProviders.d.ts +7 -0
- package/dist/repaintScheduler.d.ts +29 -1
- package/dist/repaintScheduler.js +149 -48
- package/dist/resolveApiUrl.js +3 -0
- package/docs/bluetooth.md +191 -0
- package/docs/cli.md +131 -0
- package/docs/design/roadmap.md +9 -0
- package/docs/design/spec.md +59 -0
- package/docs/examples/README.md +25 -0
- package/docs/examples/tide-clock.md +93 -0
- package/docs/examples/watch-schedule.md +37 -0
- package/docs/extending.md +33 -0
- package/docs/faq.md +44 -0
- package/docs/getting-started.md +111 -0
- package/docs/templates.md +142 -0
- package/package.json +18 -8
package/docs/cli.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Command Line Interface
|
|
2
|
+
|
|
3
|
+
To get fast feedback on templates and shelf devices without updating and configuring SignalK, a CLI call `esl-cli` is provided when the module is manually installed (see [Using Outside of SignalK](getting-started.md#using-outside-of-signalk)) that has these commands. Use `--help` to get all the options.
|
|
4
|
+
|
|
5
|
+
- `vendors` - list supported vendors
|
|
6
|
+
- `scan` - report supported devices found from a BLE scan
|
|
7
|
+
- `render` - transform an SVG template and data into a PNG
|
|
8
|
+
- `paint` - render an SVG template and data to a selected ESL
|
|
9
|
+
- `fields` and `field` - see [Debugging Templates](#debugging-templates)
|
|
10
|
+
|
|
11
|
+
The width, height, vertical offset and colour palette for the device are taken from the internal register of devices, however can be overridden on the command line with `-w/--width`, `--height`, `--voffset` and `--colours`. This could be used to help you choose what size of label to buy, or to get an unsupported label working.
|
|
12
|
+
|
|
13
|
+
Left unset, both `render` and `paint` default `-w/--width`/`--height` to the template's own declared `width`/`height` (or `viewBox`) - neither command connects to a device just to size the render, since that would mean an extra BLE connect ahead of `paint`'s own, and doing two back-to-back is exactly the kind of churn that trips real BLE hardware.
|
|
14
|
+
|
|
15
|
+
`paint` also takes `--reframe <mode>`, applied once it has connected and identified the device, for when the rendered image doesn't come out the same size as its actual panel (see [Reframing](templates.md#reframing)):
|
|
16
|
+
|
|
17
|
+
- `crop` (default) - keeps pixels 1:1, placed from the top-left; a bigger render is truncated to fit, a smaller one leaves the extra panel space blank
|
|
18
|
+
- `scale` - stretches the rendered image onto the panel's exact dimensions (independently per axis, not preserving aspect ratio)
|
|
19
|
+
- `fixed` - no adjustment; rejects a size mismatch with an error instead
|
|
20
|
+
|
|
21
|
+
The main SignalK plugin offers the same choice per device (defaulting to `crop` there too) in each device's own config - "If the render doesn't match the panel size".
|
|
22
|
+
|
|
23
|
+
`paint` has matching options for the other per-label image settings too (see [Other Image Options](templates.md#other-image-options)):
|
|
24
|
+
|
|
25
|
+
- `--mirror <mode>` - `none` (default), `horizontal`, `vertical`, or `both` (rotate 180°). `render` also takes `--mirror`, to preview the flip as a PNG without a label
|
|
26
|
+
- `--no-compress` - send the image uncompressed, to rule compression out if a label won't update
|
|
27
|
+
- `--compression-format <format>` - `auto` (default) or `chunked`, to try the experimental compressed format on a Gicisky 4.2" BWR
|
|
28
|
+
|
|
29
|
+
`esl-cli` can also be extended with new subcommands - see [Extending](extending.md#cli-commands).
|
|
30
|
+
|
|
31
|
+
( The CLI can also be run from a checked out module, or by opening a terminal shell at `~/.signalk/node_modules/@rhizomatics/signalk-einklabel-plugin`, as `npx esl-cli command --args` )
|
|
32
|
+
|
|
33
|
+
## Scans from CLI
|
|
34
|
+
|
|
35
|
+
The command line tools, run from inside the `.signalk` directory, can be used to help troubleshoot
|
|
36
|
+
|
|
37
|
+
- Scan for longer, in this example 90 seconds
|
|
38
|
+
- `npx esl-cli scan -d 90`
|
|
39
|
+
- Scan for all BLE devices, whatever they are
|
|
40
|
+
- `npx esl-cli scan -a`
|
|
41
|
+
|
|
42
|
+
## Debugging Templates
|
|
43
|
+
|
|
44
|
+
The `esl-cli` can be used to debug and validate templates quickly:
|
|
45
|
+
|
|
46
|
+
- `render` - Render templates with SignalK data and write to a local PNG file
|
|
47
|
+
- `paint` - Render templates with SignalK data and send to selected ESL device
|
|
48
|
+
- `fields` - List the fields in the template, with the source specification and the rendered data value
|
|
49
|
+
- `field` - Accept a source specification (outside of any template context) and return the rendered value if available
|
|
50
|
+
|
|
51
|
+
Use `--help` to get the full set of arguments for any of the commands.
|
|
52
|
+
|
|
53
|
+
## CLI Examples
|
|
54
|
+
|
|
55
|
+
### Paint Image Directly
|
|
56
|
+
|
|
57
|
+
The label address previously discovered via `esl-cli scan`
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
npx esl-cli paint -t templates/tides/250x128-BWRY.svg -a FF:FF:92:84:53:93
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
If the label turns out to be a different size than the template (e.g. it's a 250x128 template on a 416x240 panel), it's cropped to fit by default - add `--reframe scale` to stretch it instead:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
npx esl-cli paint -t templates/tides/250x128-BWRY.svg -a FF:FF:92:84:53:93 --reframe scale
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
If a label doesn't update, try sending it uncompressed to see whether compression is the cause:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
npx esl-cli paint -t templates/tides/250x128-BWRY.svg -a FF:FF:92:84:53:93 --no-compress
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
If the image comes out mirrored, try each `--mirror` mode until it looks right, then set the same _Mirror_ option in the label's config:
|
|
76
|
+
|
|
77
|
+
```bash
|
|
78
|
+
npx esl-cli paint -t templates/tides/250x128-BWRY.svg -a FF:FF:92:84:53:93 --mirror horizontal
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Test Template Without Updating Label
|
|
82
|
+
|
|
83
|
+
This will work even if you don't have a label, or even bluetooth. (The `-u` can be left out if your SignalK server running locally on default ports).
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
npx esl-cli render -t templates/tides/250x128-BWRY.svg -o example.png -u http://localhost
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
and this version will work even without a running SignalK server, using some pre-packaged example data:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
npx esl-cli render -t templates/tides/250x128-BWRY.svg -o example.png -e examples
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### List all Fields and Rendered Values
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
npx esl-cli fields -t templates/tide.svg -u http://localhost
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
id spec value
|
|
103
|
+
station.name source=resources,resource=tides,provider=tides,path=station.name Tobermory
|
|
104
|
+
source.name. source=resources,resource=tides,provider=tides,path=station.source.name TICON-4
|
|
105
|
+
last_repaint source=einklabel,path=repainted,format=local_datetime_short 30 Jun 26 00:08
|
|
106
|
+
extremes.0 source=resources,resource=tides,provider=tides,path=extremes[0].label Low
|
|
107
|
+
extremes.1 source=resources,resource=tides,provider=tides,path=extremes[1].label High
|
|
108
|
+
extremes.2 source=resources,resource=tides,provider=tides,path=extremes[2].label Low
|
|
109
|
+
timezoneRegion source=einklabel,path=local_zone BST
|
|
110
|
+
lat source=resources,resource=tides,provider=tides,path=station.datums.LAT,category=depth 0.2m
|
|
111
|
+
hat source=resources,resource=tides,provider=tides,path=station.datums.HAT,category=depth 5.2m
|
|
112
|
+
extremes.2.level source=resources,resource=tides,provider=tides,path=extremes[2].level,category=depth 1.1m
|
|
113
|
+
extremes.2.time source=resources,resource=tides,provider=tides,path=extremes[2].time,format=local_time 13:15
|
|
114
|
+
extremes.1.level source=resources,resource=tides,provider=tides,path=extremes[1].level,category=depth 3.8m
|
|
115
|
+
extremes.1.time source=resources,resource=tides,provider=tides,path=extremes[1].time,format=local_time 07:05
|
|
116
|
+
extremes.0.time source=resources,resource=tides,provider=tides,path=extremes[0].time,format=local_time 01:21
|
|
117
|
+
extremes.0.time-8 source=resources,resource=tides,provider=tides,path=extremes[0].time,format=day_mon 30 Jun
|
|
118
|
+
extremes.0.time-8-5 source=resources,resource=tides,provider=tides,path=extremes[1].time,format=day_mon 30 Jun
|
|
119
|
+
extremes.0.time-8-9 source=resources,resource=tides,provider=tides,path=extremes[2].time,format=day_mon 30 Jun
|
|
120
|
+
extremes.0.level source=resources,resource=tides,provider=tides,path=extremes[0].level,category=depth 1.3m
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Offline Working
|
|
124
|
+
|
|
125
|
+
`render` and `paint` need a `--url` argument to point to the SignalK server to retrieve data. If you don't have access to one, you can use `--example-data` or `-e` to point to a directory of example data, which is bundled with the plugin or available in GitHub at [examples](https://github.com/rhizomatics/signalk-einklabel-plugin/tree/main/examples). This also allows you to write templates for resource APIs that aren't available yet.
|
|
126
|
+
|
|
127
|
+
- `vessels.json` - The standard SignalK vessel paths
|
|
128
|
+
- `resources/xxxx.json` - The output of the `xxxx` resources API call
|
|
129
|
+
- `categories.json` - SignalK unit categories needed for `category=depth` type formatting
|
|
130
|
+
|
|
131
|
+
For example, `npx esl-cli fields -t templates/tide.svg -e examples` will show all the field data that will be populated from the example API, vessel and category data in the `examples` local directory.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Design Plans
|
|
2
|
+
|
|
3
|
+
## Label Support
|
|
4
|
+
|
|
5
|
+
- More vendors, and more labels for existing vendors
|
|
6
|
+
- Ability for people to plug-in vendor support for themselves or as published plugins
|
|
7
|
+
- A model override in the plugin config, so a discovered device whose PID isn't in its driver's table yet can be repainted (the CLI's `paint --colours` already does this)
|
|
8
|
+
- Neaps or springs status (e.g. "Springs +1", "Neaps -2") on the tide template, pending upstream signalk-tides support
|
|
9
|
+
- Alternatives to SVG templates, perhaps [imagespec](https://pypi.org/project/imagespec/) or [signalk-hmi-designer](https://github.com/dirkwa/signalk-hmi-designer)
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Design Specification
|
|
2
|
+
|
|
3
|
+
A SignalK plugin, in TypeScript, that renders selected SignalK data to BLE eInk Electronic Shelf Labels (ESLs). This page describes the plugin as it is built. Plans and ideas not yet built are in the [roadmap](roadmap.md).
|
|
4
|
+
|
|
5
|
+
# Features
|
|
6
|
+
|
|
7
|
+
- Native TypeScript, Node 20 compatible (runs on armv7, e.g. a Cerbo GX)
|
|
8
|
+
- Bundled templates for a tide clock and a watch schedule, rendered from any SignalK data, Resources API resource or label fact a template binds to
|
|
9
|
+
- Pluggable vendor support
|
|
10
|
+
- Built-in drivers for Zhsunyco and Gicisky BLE labels (`src/devices/zhsunyco/`, `src/devices/gicisky/`), registered by `plugin.ts` at startup
|
|
11
|
+
- Each driver has a metadata table of the models it supports, keyed by PID, with `hwVersion` used where a vendor reuses a PID across panel sizes. Each entry gives the label, width, height, vertical offset and colours. `esl-cli vendors` lists them
|
|
12
|
+
- Additional vendors are added by a separate npm package implementing `VendorDriver` and calling the exported `registerVendorDriver` explicitly - no scanning of installed packages. In the SignalK runtime the extension calls it from its own plugin `start()`; the CLI loads it via `esl-cli --require <module>`. Extension packages declare this package as a regular `dependency`, not a `peerDependency` (SignalK's guidance is that npm's peer-dependency resolution interacts poorly with the server's plugin install layout), and declare the SignalK-level relationship via `"signalk": { "requires": ["@rhizomatics/signalk-einklabel-plugin"] }` - see [Extending](../extending.md)
|
|
13
|
+
- The config schema's device and template options are read live from the registries when the schema is built, so a newly-registered extension shows up the next time the config panel is opened
|
|
14
|
+
- Bluetooth access, either direct or shared
|
|
15
|
+
- Direct BlueZ access over D-Bus via `node-ble` (the default). The plugin waits, with backoff, for a local adapter before scanning or painting, to cover the boot-time race where the plugin starts before BlueZ is ready
|
|
16
|
+
- The SignalK BLE Manager API (`app.bleApi`, server >= 2.32.0), opted into with `useBleApi`, so the plugin shares the adapter with other BLE plugins. Only offered in the config schema when the running server has it. Paints through BLE Manager are serialised so only one label holds a GATT connection at a time
|
|
17
|
+
- Device discovery
|
|
18
|
+
- One shared discovery path across all registered drivers (`discoveryCoordinator.ts`): each advertisement is read once and offered to each driver's `matchesAdvertisement`; only a matching driver's `identifyDevice` runs, which may open a GATT connection when the advertisement alone doesn't identify the model
|
|
19
|
+
- A scan already in progress is joined rather than a second one started, since neither backend can run two discovery sessions at once
|
|
20
|
+
- With direct BlueZ access, scanning happens in bounded windows: an optional startup scan (`scanOnStart`, off by default, `scanDurationSeconds` long), an on-demand scan when an "All discovered devices" entry has nothing discovered yet, or `esl-cli scan`. With BLE Manager, the plugin listens to its advertisement stream for as long as the plugin runs, and only identifies new addresses
|
|
21
|
+
- Discovered devices are persisted in the plugin data directory, keyed by BLE address, and kept for 24 hours after last being seen; a successful paint counts as being seen. The startup scan reports what it found in the plugin status line
|
|
22
|
+
- The device picker lists any device a driver identified as its vendor, even if the PID isn't in that driver's metadata table yet (labelled "vendor unrecognised PID 0x.... (address)"), so it can be reported. Such a device can't be repainted from the plugin, since there's no width/height to render with; `esl-cli paint` can paint one by supplying the model facts on the command line
|
|
23
|
+
- Device configuration
|
|
24
|
+
- Each device entry has a friendly name, a combined "Device" choice, an optional location/description, a template and a repaint trigger, with less common settings grouped under "Advanced settings"
|
|
25
|
+
- The "Device" value is `"<vendor>:<pid>[:<hwVersion>]@<address>"`, picked from recently discovered devices, so width/height/colours are known from config alone and never need a live BLE read or a rescan after a restart. The special value `ALL` ("All discovered devices") paints the same template and trigger to every currently-known device instead, resolved at repaint time
|
|
26
|
+
- Advanced settings: reframe (`crop` by default, `scale`, or `fixed` to fail on a size mismatch), compress upload (on by default), mirror (`none`, `horizontal`, `vertical`, `both`), Gicisky wire format (`auto` or experimental `chunked`), Zhsunyco write-without-response, force repaint, Zhsunyco AES key (falls back to a default key), and per-device overrides of the plugin-wide paint connect timeout and retry count
|
|
27
|
+
- Config saved before advanced settings were grouped, or nested by an older save bug, is rewritten into the current shape at plugin start (`healStoredConfig`)
|
|
28
|
+
- Templates
|
|
29
|
+
- SVG files, rendered to bitmaps with resvg-wasm (`src/render/svgRenderer.ts`). A user templates directory (`templatesDir`: empty for `~/.signalk/einklabel/templates`, relative to `~/.signalk`, or absolute) is scanned alongside the bundled `templates/` directory; a user template with the same name as a bundled one takes priority. There is no upload UI
|
|
30
|
+
- A template is either a single `.svg` file or a _template family_: a directory of variants named `<width>x<height>-<colours>.svg` (e.g. `tides/416x240-BWRY.svg`). The best variant for each device is picked at repaint time: exact size and colours, then exact size, then the nearest width with the closest aspect ratio. Dot-prefixed directories (`.assets`, `.blank`, `.error`) are never offered as templates
|
|
31
|
+
- Each template can be assigned to one or more devices. There is no separate data config; a template's own bindings declare what data it needs, re-read from the template on every repaint
|
|
32
|
+
- Text uses the bundled serif, sans-serif and monospace fonts (resvg-wasm can't see system fonts); the generic CSS family names are rewritten to the bundled fonts' exact names
|
|
33
|
+
- Bindings
|
|
34
|
+
- A dynamic `<text>` element carries a flat `key=value,key=value` binding in a `<desc>` child, not a templating engine - e.g. `source=resources,resource=tides,path=extremes[0].time,format=local_time`. `source`/`context` default to `signalk`/`self`, a bare path with no `=` is shorthand for `path=...`, and array indices use bracket notation. The element's own text is a placeholder for laying out the template in an editor. See `src/render/binding.ts` for the grammar
|
|
35
|
+
- Keys: `source`, `context`, `resource`, `provider`, `path`, `format`, `category`, `round`, `assets`, `default`. An unknown key is an error; `default=` replaces a missing value
|
|
36
|
+
- `source=signalk` (default): `context=self` reads via `getSelfPath`; any other `context` is read via `getPath` against that literal SignalK context, exactly as shown in the Data Browser (e.g. `vessels.urn:mrn:imo:mmsi:232345678`)
|
|
37
|
+
- `source=resources`: `resource=<name>` reads any Resources API resource type (`tides`, `waypoints`, ...) via `app.resourcesApi.listResources` in-process. `provider=<plugin id>` pins it to one resource provider; a pinned binding goes over the REST API instead, since the in-process call ignores the provider id (signalk-server 2.30.0)
|
|
38
|
+
- `source=label`: facts about the physical label - `manufacturer`, `label`, `width`, `height`, `colours`, `fonts`, `description`, and `position` (rounded to about 1km so GPS jitter doesn't force repaints)
|
|
39
|
+
- `source=einklabel`: plugin-injected values - `repainted` (repaint time), `local_zone` (timezone abbreviation) and `plugin_version`
|
|
40
|
+
- `format=` names a formatter from `src/render/formatters.ts`: `local_time`, `day_mon`, `local_datetime_short`, `utc_offset`, `position`, `csv`. Times use the vessel's `environment.time.timezoneRegion` when published, otherwise the host's timezone - never a tide station's own `timezone` field
|
|
41
|
+
- A `signalk`-sourced numeric value converts to its path's preferred display unit by default, via `GET {signalkApiUrl}/signalk/v1/api/vessels/<context>/meta` (there's no in-process equivalent); `format=raw` opts out. A value with no path of its own (e.g. resource-sourced) can take an explicit `category=<name>` (e.g. `category=depth`), resolved from `{signalkApiUrl}/signalk/v1/unitpreferences/...` client-side (`src/unitCategories.ts`). `round=` sets decimal places
|
|
42
|
+
- An `<image>` element with a `<desc>` binding and an `assets=<name>` key picks one `.svg` from an asset directory (`.assets/<name>/`, user templates directory first, then bundled) by the resolved value, e.g. the bundled `lunar_phases` icons for `environment.moon.phaseName`. The element is dropped when the value is missing or has no matching file
|
|
43
|
+
- SignalK API base URL
|
|
44
|
+
- `signalkApiUrl` is optional. Left unset, `resolveSignalkApiUrl` (`src/resolveApiUrl.ts`) probes `http://localhost:3000`, `http://localhost` and `https://localhost`, in that order, at the first repaint and caches whichever responds. Always the loopback address, since the plugin runs on the same host as the server. Setting it explicitly skips probing but is still verified
|
|
45
|
+
- It errors clearly if nothing responds (wrong port) or every probe is rejected (anonymous read access not enabled) - these endpoints need anonymous read access, since the plugin has no login flow. A failed resolution is retried on the next repaint rather than cached
|
|
46
|
+
- Path metadata is best-effort (no server just means raw values); a `category=` or `provider=` binding without a reachable server fails the render
|
|
47
|
+
- Repainting
|
|
48
|
+
- Each device picks one repaint trigger: a change on a SignalK path (`subscription`, debounced by 2 seconds, ignoring transient null/missing values), or a fixed interval (every N hours at a configurable minute past the hour, checked by one shared one-minute timer rather than one per device)
|
|
49
|
+
- Every trigger waits for a settle window after plugin start (`settleSeconds`, default 120), so a template isn't rendered before other plugins (e.g. derived-data) have published its data. After that window each device is checked once; an interval device already painted for its current slot skips this catch-up
|
|
50
|
+
- Before repainting, the template's mtime and the assembled data (bindings plus label facts, before plugin-injected values like the repaint time are added) are hashed and compared against the last successful paint, persisted in the plugin data directory per device entry and BLE address (`friendlyName:address`). If both are unchanged the repaint is skipped, to save the label's battery and avoid a full-panel flash. A template provider's output always repaints, since the plugin can't know whether it would differ
|
|
51
|
+
- "Force repaint" bypasses the unchanged-data skip for one repaint, then clears itself once every target device has painted successfully
|
|
52
|
+
- Only one repaint per device entry runs at a time; a trigger arriving mid-repaint queues one follow-up, run only if the current repaint succeeded
|
|
53
|
+
- A paint is retried up to `paintRetries` times (default 3, 5 seconds apart), each attempt bounded by `paintConnectTimeoutSeconds` (default 30) plus a backstop deadline
|
|
54
|
+
- Stopping the plugin (a config save, disable, or server shutdown) stops every trigger, drops queued follow-ups and retries, and skips a paint that hasn't connected yet. `stop()` returns a Promise that resolves once a paint already under way has finished, waiting at most 30 seconds, and the next `start()` also waits for it, so two BLE sessions don't overlap
|
|
55
|
+
- If rendering fails (a broken template, a missing file, or a template provider's `render()` rejecting), the bundled `.error` warning template is painted instead, so stale content is never left on screen unmarked. No success hash is saved, so the next trigger retries
|
|
56
|
+
- Template providers
|
|
57
|
+
- An extension package can offer content generated another way, alongside SVG templates, via `registerTemplateProvider` - e.g. `@rhizomatics/signalk-einklabel-genai-plugin` generating content from an LLM prompt. Its entries appear in the same "Template" dropdown, marked by the provider's `suffix`. See [Extending](../extending.md) and `src/render/templateProviders.ts`
|
|
58
|
+
- `esl-cli`, a local CLI for scanning, painting and testing templates outside the SignalK plugin
|
|
59
|
+
- `vendors`, `scan`, `paint` (with the same image options as the plugin, plus `--colours`/`--width`/`--height`/`--voffset` model facts for hardware not yet in a driver's table), `render` (template to PNG), `fields` (every binding in a template with its resolved value) and `field` (one binding spec). Bindings resolve against a live SignalK server over HTTP, or against local example JSON (`-e examples`). Extensions can add subcommands via `--require`. See [Command Line Interface](../cli.md)
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
# Examples
|
|
2
|
+
|
|
3
|
+
The plugin comes with ready-made templates for these examples. Pick one as a label's _Template_ in the plugin config, or copy it into your own templates directory as a starting point - see [Templates](../templates.md).
|
|
4
|
+
|
|
5
|
+
- [Tide Clock](tide-clock.md) - the next few high and low tides, with the moon phase
|
|
6
|
+
- [Watch Schedule](watch-schedule.md) - who's on watch now and next
|
|
7
|
+
|
|
8
|
+
## Bundled Templates
|
|
9
|
+
|
|
10
|
+
Every bundled template, with its sizes and the data it needs. Each example's page lists the exact labels each size fits and every field it reads.
|
|
11
|
+
|
|
12
|
+
<!-- BEGIN GENERATED: templates-summary -->
|
|
13
|
+
<!-- Generated from the bundled templates by `npm run docs:templates` - do not edit by hand. -->
|
|
14
|
+
|
|
15
|
+
| Template | Sizes | Data needed |
|
|
16
|
+
| ---------------------------- | ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
17
|
+
| [`tide.svg`](tide-clock.md) | 416×240 | `tides` resource (provider `tides`), Signal K `environment.moon.phaseName` |
|
|
18
|
+
| [`tides`](tide-clock.md) | 416×240, 296×128, 250×128 | `tides` resource (provider `tides`), Signal K `environment.moon.phaseName` |
|
|
19
|
+
| [`watch`](watch-schedule.md) | 416×240 | Signal K `watch.current.endTime`, `watch.current.startTime`, `watch.current.teamName`, `watch.next.endTime`, `watch.next.startTime`, `watch.next.teamName`, `watch.system.name` |
|
|
20
|
+
|
|
21
|
+
<!-- END GENERATED -->
|
|
22
|
+
|
|
23
|
+
## Trying Examples Without a Boat
|
|
24
|
+
|
|
25
|
+
Each template can be rendered to a PNG with the CLI using the bundled example data, with no SignalK server or label needed - see [Test Template Without Updating Label](../cli.md#test-template-without-updating-label).
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Tide Clock
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Template available as 416x240-BWRY for 3.7" ESLs and a simpler template, sized 250x128, also BWRY, for the cheapest 2.13" labels.
|
|
6
|
+
|
|
7
|
+
This mini tide clock is a 2.9" Gicisky device, less than £10 inc delivery in summer 2026.
|
|
8
|
+
|
|
9
|
+

|
|
10
|
+
|
|
11
|
+
## Pre-requisites
|
|
12
|
+
|
|
13
|
+
A _tides_ provider plugin for the Resources API installed and enabled, currently one of:
|
|
14
|
+
|
|
15
|
+
- [signalk-tides](https://github.com/openwatersio/signalk-tides) - uses [neaps](https://github.com/openwatersio/neaps) library for international off-line coverage
|
|
16
|
+
- [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_
|
|
17
|
+
|
|
18
|
+
For versions with the moon phases:
|
|
19
|
+
|
|
20
|
+
- [signalk-derived-data](https://github.com/SignalK/signalk-derived-data) - unlike other plugins, this publishes lunar and solar facts as SignalK paths
|
|
21
|
+
|
|
22
|
+
The moon phase is optional - without it, the moon icon is simply left blank and the rest of the tide clock still shows.
|
|
23
|
+
|
|
24
|
+
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.
|
|
25
|
+
|
|
26
|
+
- 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 it's the preferred `signalk-tides` provider and makes it a simple local time rather than a UTC date-time.
|
|
27
|
+
|
|
28
|
+
To show the lunar phase, the `environment.moon.phaseName` path is required, which can be easily achieved by installing and configuring the `derived-data` plugin.
|
|
29
|
+
|
|
30
|
+
> [!TIP]
|
|
31
|
+
> If testing this without a boat, you'll need another plugin to provide `navigation.position` and `navigation.datetime` to make the moon and tide calcuations work. [signalk-datetime](https://github.com/tmcolby/signalk-datetime) can be configured for the datetime, and [signalk-sailboat-simulator](https://github.com/macjl/signalk-sailboat-simulator) for the postion; other ways may work too.
|
|
32
|
+
|
|
33
|
+
## Template Reference
|
|
34
|
+
|
|
35
|
+
The `tides` template family picks the best size for each label automatically - see [Template Families](../templates.md#template-families-multiple-panel-sizescolours).
|
|
36
|
+
|
|
37
|
+
<!-- BEGIN GENERATED: template-reference tides -->
|
|
38
|
+
<!-- Generated from the bundled templates by `npm run docs:templates` - do not edit by hand. -->
|
|
39
|
+
|
|
40
|
+
**Template:** `tides`
|
|
41
|
+
|
|
42
|
+
| File | Size (px) | Aspect ratio | Colours | Fits these labels exactly |
|
|
43
|
+
| ------------------------------------------------------------------------------------------------------------------------------ | --------- | ------------ | ------------------------- | ------------------------------------------------------------------------ |
|
|
44
|
+
| [`tides/416x240-BWRY.svg`](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tides/416x240-BWRY.svg) | 416 × 240 | 1.73 : 1 | black, white, red, yellow | Zhsunyco 3.7" BWRY |
|
|
45
|
+
| [`tides/296x128-BWRY.svg`](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tides/296x128-BWRY.svg) | 296 × 128 | 2.31 : 1 | black, white, red, yellow | Zhsunyco 2.9" BWRY, Gicisky 2.9" BW, Gicisky 2.9" BWR, Gicisky 2.9" BWRY |
|
|
46
|
+
| [`tides/250x128-BWRY.svg`](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tides/250x128-BWRY.svg) | 250 × 128 | 1.95 : 1 | black, white, red, yellow | Zhsunyco 2.13" BWRY, Gicisky 2.1" BWR |
|
|
47
|
+
|
|
48
|
+
**Data used**
|
|
49
|
+
|
|
50
|
+
| Source | Path | Options | Used in |
|
|
51
|
+
| ----------------------------------- | ---------------------------- | ------------------------------------- | -------------------------- |
|
|
52
|
+
| `tides` resource (provider `tides`) | `extremes[n].label` | - | all |
|
|
53
|
+
| `tides` resource (provider `tides`) | `extremes[n].level` | category `depth` | all |
|
|
54
|
+
| `tides` resource (provider `tides`) | `extremes[n].time` | format `local_time`, format `day_mon` | all |
|
|
55
|
+
| `tides` resource (provider `tides`) | `station.datums.HAT` | category `depth`, round 1 | 416x240-BWRY |
|
|
56
|
+
| `tides` resource (provider `tides`) | `station.datums.LAT` | category `depth`, round 1 | 416x240-BWRY |
|
|
57
|
+
| `tides` resource (provider `tides`) | `station.name` | - | all |
|
|
58
|
+
| `tides` resource (provider `tides`) | `station.source.name` | - | 416x240-BWRY, 296x128-BWRY |
|
|
59
|
+
| Plugin | `local_zone` | - | 416x240-BWRY, 296x128-BWRY |
|
|
60
|
+
| Plugin | `plugin_version` | - | 416x240-BWRY |
|
|
61
|
+
| Plugin | `repainted` | format `local_datetime_short` | all |
|
|
62
|
+
| Signal K path | `environment.moon.phaseName` | image from `lunar_phases` | 416x240-BWRY |
|
|
63
|
+
|
|
64
|
+
<!-- END GENERATED -->
|
|
65
|
+
|
|
66
|
+
The original single-file version is also still available as `tide.svg`:
|
|
67
|
+
|
|
68
|
+
<!-- BEGIN GENERATED: template-reference tide.svg -->
|
|
69
|
+
<!-- Generated from the bundled templates by `npm run docs:templates` - do not edit by hand. -->
|
|
70
|
+
|
|
71
|
+
**Template:** `tide.svg`
|
|
72
|
+
|
|
73
|
+
| File | Size (px) | Aspect ratio | Colours | Fits these labels exactly |
|
|
74
|
+
| -------------------------------------------------------------------------------------------------- | --------- | ------------ | ------- | ------------------------- |
|
|
75
|
+
| [`tide.svg`](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tide.svg) | 416 × 240 | 1.73 : 1 | - | Zhsunyco 3.7" BWRY |
|
|
76
|
+
|
|
77
|
+
**Data used**
|
|
78
|
+
|
|
79
|
+
| Source | Path | Options |
|
|
80
|
+
| ----------------------------------- | ---------------------------- | ------------------------------------- |
|
|
81
|
+
| `tides` resource (provider `tides`) | `extremes[n].label` | - |
|
|
82
|
+
| `tides` resource (provider `tides`) | `extremes[n].level` | category `depth` |
|
|
83
|
+
| `tides` resource (provider `tides`) | `extremes[n].time` | format `local_time`, format `day_mon` |
|
|
84
|
+
| `tides` resource (provider `tides`) | `station.datums.HAT` | category `depth`, round 1 |
|
|
85
|
+
| `tides` resource (provider `tides`) | `station.datums.LAT` | category `depth`, round 1 |
|
|
86
|
+
| `tides` resource (provider `tides`) | `station.name` | - |
|
|
87
|
+
| `tides` resource (provider `tides`) | `station.source.name` | - |
|
|
88
|
+
| Plugin | `local_zone` | - |
|
|
89
|
+
| Plugin | `plugin_version` | - |
|
|
90
|
+
| Plugin | `repainted` | format `local_datetime_short` |
|
|
91
|
+
| Signal K path | `environment.moon.phaseName` | image from `lunar_phases` |
|
|
92
|
+
|
|
93
|
+
<!-- END GENERATED -->
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
# Watch Schedule
|
|
2
|
+
|
|
3
|
+

|
|
4
|
+
|
|
5
|
+
Template available as 416x240-BWRY for 3.7" ESLs.
|
|
6
|
+
|
|
7
|
+
## Pre-requisites
|
|
8
|
+
|
|
9
|
+
- Source of `watch.current` and `watch.next` values
|
|
10
|
+
- `signalk-watch-schedule` plugin
|
|
11
|
+
|
|
12
|
+
## Template Reference
|
|
13
|
+
|
|
14
|
+
<!-- BEGIN GENERATED: template-reference watch -->
|
|
15
|
+
<!-- Generated from the bundled templates by `npm run docs:templates` - do not edit by hand. -->
|
|
16
|
+
|
|
17
|
+
**Template:** `watch`
|
|
18
|
+
|
|
19
|
+
| File | Size (px) | Aspect ratio | Colours | Fits these labels exactly |
|
|
20
|
+
| ------------------------------------------------------------------------------------------------------------------------------ | --------- | ------------ | ------------------------- | ------------------------- |
|
|
21
|
+
| [`watch/416x240-BWRY.svg`](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/watch/416x240-BWRY.svg) | 416 × 240 | 1.73 : 1 | black, white, red, yellow | Zhsunyco 3.7" BWRY |
|
|
22
|
+
|
|
23
|
+
**Data used**
|
|
24
|
+
|
|
25
|
+
| Source | Path | Options |
|
|
26
|
+
| ------------- | ------------------------- | ----------------------------- |
|
|
27
|
+
| Plugin | `plugin_version` | - |
|
|
28
|
+
| Plugin | `repainted` | format `local_datetime_short` |
|
|
29
|
+
| Signal K path | `watch.current.endTime` | format `local_time` |
|
|
30
|
+
| Signal K path | `watch.current.startTime` | format `local_time` |
|
|
31
|
+
| Signal K path | `watch.current.teamName` | - |
|
|
32
|
+
| Signal K path | `watch.next.endTime` | format `local_time` |
|
|
33
|
+
| Signal K path | `watch.next.startTime` | format `local_time` |
|
|
34
|
+
| Signal K path | `watch.next.teamName` | - |
|
|
35
|
+
| Signal K path | `watch.system.name` | - |
|
|
36
|
+
|
|
37
|
+
<!-- END GENERATED -->
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# Extending
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
|
|
5
|
+
The primary things managed and provided by the plugin are:
|
|
6
|
+
|
|
7
|
+
- ESL Vendor
|
|
8
|
+
- Sub-package per vendor
|
|
9
|
+
- ESL Device
|
|
10
|
+
- Metadata in the vendor package, using a `pid` or sometimes `pid` combined with `hwid` in the BLE results to pinpoint a model
|
|
11
|
+
- SVG Template
|
|
12
|
+
- SignalK API base URL
|
|
13
|
+
- Used for automatic unit conversion on `signalk`-sourced numeric values and for resolving an explicit `category=` binding - neither has an in-process equivalent, both go via this server's own REST API
|
|
14
|
+
- Optional: left blank, the plugin probes the probable values in likelihood order at startup - `http://localhost:3000`, `http://localhost`, `https://localhost`. Set it explicitly to skip probing
|
|
15
|
+
- Either way, errors clearly if nothing responds (wrong port) or the probe is rejected (anonymous read access not enabled) - these endpoints must allow anonymous read access, since the plugin has no login flow
|
|
16
|
+
|
|
17
|
+
## Hardware
|
|
18
|
+
|
|
19
|
+
Additional vendors and devices can be added by a separate npm package that implements the `VendorDriver` interface and registers itself - there's no scanning of installed packages, registration is always an explicit call by the extension's own code.
|
|
20
|
+
|
|
21
|
+
- `import esl from '@rhizomatics/signalk-einklabel-plugin'; esl.registerVendorDriver(myDriver)`
|
|
22
|
+
- In the SignalK runtime, call this from the extension's own plugin `start()`. In the CLI, load the extension with `esl-cli --require <module> <command>`.
|
|
23
|
+
- Declare this package as a regular npm `dependency` in the extension package - **not** a `peerDependency`, per SignalK's own guidance that npm's peer-dependency resolution interacts poorly with the server's plugin install layout - and declare the SignalK-level relationship via `"signalk": { "requires": ["@rhizomatics/signalk-einklabel-plugin"] }` in the extension's own `package.json` instead, so the App Store can install/report it.
|
|
24
|
+
|
|
25
|
+
## Template Providers
|
|
26
|
+
|
|
27
|
+
An alternative way to produce a device's content, alongside hand-authored SVG templates, can be added the same way - `esl.registerTemplateProvider({ suffix, listTemplates, render })`, from a separate package's own plugin `start()` (or `esl-cli --require <module>`). This is what [`@rhizomatics/signalk-einklabel-genai-plugin`](templates.md#genai-rendering) is: `listTemplates()` returns the entries it currently offers (already including its own `suffix`, e.g. `"forecast (GenAI)"`) for the "Template" dropdown, and `render(request)` - given the same signalk/resources/label context any SVG template's bindings get - returns a `Bitmap`, exactly as `new esl.Renderer().render()` would for an SVG template. Rejecting from `render()` routes the repaint through this plugin's own generic fallback-warning template, the same as a broken hand-authored template failing to render. An optional `describeContent(request)` returns a human-readable description of what `render()` will be given (the GenAI plugin returns its fully-substituted LLM prompt); when present, it's logged to the plugin's debug output just before each `render()`, standing in for the `<desc>` bindings an SVG template's debug output would show.
|
|
28
|
+
|
|
29
|
+
`esl.Renderer` (the SVG renderer), `esl.bitmapToPng`, `esl.buildLabel`, `esl.findBindingsInText`, and `esl.substituteBindingsInText` are also exported for a `TemplateProvider` extension to reuse - e.g. to resolve its own `{...}`-style placeholders against the request's context, or to rasterize whatever SVG it produces into the `Bitmap` its `render()` must return.
|
|
30
|
+
|
|
31
|
+
## CLI Commands
|
|
32
|
+
|
|
33
|
+
`esl-cli` can also be extended with new subcommands by a `-r/--require`'d package, which is how [`@rhizomatics/signalk-einklabel-genai-plugin`](templates.md#genai-rendering) adds its own `prompt`/`generate` commands for testing prompts without a device. See [Command Line Interface](cli.md) for the built-in commands.
|
package/docs/faq.md
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# Frequently Asked Questions
|
|
2
|
+
|
|
3
|
+
## I can't see my device as a choice on the drop-down list after scan
|
|
4
|
+
|
|
5
|
+
SignalK plugins lack the 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.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
## Sometimes values are missing on the display
|
|
10
|
+
|
|
11
|
+
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.
|
|
12
|
+
|
|
13
|
+
Use the _settle_ time, to impose a minimum wait between the eInk Label plugin being initialized, and it attempting to paint any displays, and increase this value if it's still missing data.
|
|
14
|
+
|
|
15
|
+
## Times are showing incorrectly
|
|
16
|
+
|
|
17
|
+
If times are in the wrong timezone, or don't have daylight savings applied correctly,
|
|
18
|
+
then check that the server itself (at the Linux level, not SignalK, which doesn't know) is configured for your timezone, assuming of course that you're a coastal sailor. Use `raspi-config` on a Raspberry Pi, or `timedatectl` on a Linux server.
|
|
19
|
+
|
|
20
|
+
If you're a global cruiser, then use something like [signalk-set-gps-timezone](https://github.com/hoeken/signalk-set-gps-timezone) to set the value in the operating system.
|
|
21
|
+
|
|
22
|
+
## The ESL signal is too weak from my SignalK server
|
|
23
|
+
|
|
24
|
+
See [Weak Signal](bluetooth.md#weak-signal).
|
|
25
|
+
|
|
26
|
+
## Can't edit the text contents of SVG template in VSCode
|
|
27
|
+
|
|
28
|
+
If you have an SVG viewer extension, this will 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_.
|
|
29
|
+
|
|
30
|
+
## Description is set in InkScape but doesn't render
|
|
31
|
+
|
|
32
|
+
Check if the text boxes are normal text or flowed text, and correct to normal text.
|
|
33
|
+
|
|
34
|
+
## My label just shows "CONTENT UNAVAILABLE"
|
|
35
|
+
|
|
36
|
+
That's the bundled fallback warning, not necessarily an error in this plugin - it means the most recent repaint failed, whatever produced the content (a broken hand-authored template, or a `TemplateProvider` extension like [`@rhizomatics/signalk-einklabel-genai-plugin`](templates.md#genai-rendering) - e.g. its LLM call failing on no network/API access, an invalid API key, or a response that wasn't a renderable SVG, after using up its configured retries). Check the SignalK server logs (debug logging on for this plugin) for the specific error, and if it's a GenAI device, check that plugin's own provider/API key/model settings. This plugin deliberately never leaves old content on screen when a repaint fails - it retries automatically at the next scheduled interval.
|
|
37
|
+
|
|
38
|
+
## Repaints fail with "Service not available"
|
|
39
|
+
|
|
40
|
+
If a label connects but every repaint fails a couple of seconds later with `Service not available`, the label itself has most likely got stuck - take its battery out for 10-20 seconds and try again. See [Stuck Labels](bluetooth.md#stuck-labels).
|
|
41
|
+
|
|
42
|
+
## Bluetooth problems
|
|
43
|
+
|
|
44
|
+
Choosing an adapter, adapters that stop responding, and Bluetooth starting after SignalK are covered on the [Bluetooth](bluetooth.md) page.
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# Getting Started
|
|
2
|
+
|
|
3
|
+
## Pre-requisites
|
|
4
|
+
|
|
5
|
+
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.
|
|
6
|
+
|
|
7
|
+
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.
|
|
8
|
+
|
|
9
|
+
This plugin can reach BLE hardware two ways - pick whichever fits your setup:
|
|
10
|
+
|
|
11
|
+
- **Direct BlueZ access** (default) - the plugin talks to BlueZ over D-Bus itself. Requirements 1-3 below apply.
|
|
12
|
+
- **SignalK BLE Manager API** (opt-in) - SignalK server >= 2.32.0 ships a [BLE Provider/Consumer API](https://github.com/SignalK/signalk-server/issues/2411) (admin UI: "BLE Manager") that arbitrates adapter access across every BLE-consuming plugin instead of each one grabbing `hci0` for itself, and can source BLE over a remote gateway instead of local hardware at all. Enable the "Use the SignalK BLE Manager API" setting in this plugin's config once it's available (it only appears once the running server has it) - requirements 1-3 below then become the SignalK server's problem, under its own Bluetooth admin settings, not this plugin's.
|
|
13
|
+
|
|
14
|
+
1. A SignalK server, **running Linux** (direct BlueZ mode only - BLE Manager mode with a remote gateway provider has no such requirement)
|
|
15
|
+
|
|
16
|
+
- MacOS and Windows aren't supported by the [BLE interface layer](https://www.npmjs.com/package/@naugehyde/node-ble) in direct BlueZ mode, however they can be used for template development and
|
|
17
|
+
debugging (everything except `scan` and `paint`)
|
|
18
|
+
|
|
19
|
+
2. A Bluetooth adapter, that can handle BLE (Bluetooth Low Energy) - direct BlueZ mode only; in BLE Manager mode this is whatever the server's own Bluetooth settings provide.
|
|
20
|
+
|
|
21
|
+
- Bluetooth adapters for Linux can be tricky - see [Choosing a Bluetooth Adapter](bluetooth.md#choosing-a-bluetooth-adapter) for advice
|
|
22
|
+
|
|
23
|
+
3. `bluez` package installed in Linux - direct BlueZ mode only
|
|
24
|
+
|
|
25
|
+
- No need to do this if you have a Raspberry Pi with recent Raspian version, since bluez comes built in.
|
|
26
|
+
- If you're not running a Raspberry Pi, then ensure that the `dbus` package is installed
|
|
27
|
+
|
|
28
|
+
4. One or more supported Electronic Shelf Labels
|
|
29
|
+
|
|
30
|
+
- The labels used for testing this are the [Zhsunyco 3.7" BWRY](https://www.aliexpress.com/item/1005010050104435.html) and a [Gicisky 2.9" BWRY](https://www.aliexpress.com/item/1005012933325056.html) - see [Supported Labels](#supported-labels)
|
|
31
|
+
|
|
32
|
+
5. Correct time zone set on server if local time is to be shown on display
|
|
33
|
+
|
|
34
|
+
- See [Times are showing incorrectly](faq.md#times-are-showing-incorrectly)
|
|
35
|
+
- If not set, everything will work, but you may see the wrong zone or not have daylight savings applied
|
|
36
|
+
|
|
37
|
+
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.
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
Look for **eInk Label Displays** in the **SignalK AppStore** on your
|
|
42
|
+
server ( under _Apps & Plugins_ on the latest version).
|
|
43
|
+
|
|
44
|
+
### Using Outside of SignalK
|
|
45
|
+
|
|
46
|
+
The plugin can also be installed as a stand-alone module, which can be useful for designing templates away from the boat, and makes available the `esl-cli` command line tool for scanning devices and debugging templates - see [Command Line Interface](cli.md).
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install @rhizomatics/signalk-einklabel-plugin
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Configuration
|
|
53
|
+
|
|
54
|
+
Use the standard configuration option in the SignalK menu for the plugin.
|
|
55
|
+
|
|
56
|
+

|
|
57
|
+
|
|
58
|
+
## Setting up a Label
|
|
59
|
+
|
|
60
|
+
Enable the plugin, and use the large **+** sign to add a label, which opens up these fields.
|
|
61
|
+
|
|
62
|
+

|
|
63
|
+
|
|
64
|
+
- _Friendly Name_ - Give the label any name (word or phrase) you like, for example 'Tide Clock'
|
|
65
|
+
- _Device_ - Unless you have multiple labels, don't bother with pre-scanning or selecting a specific 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.
|
|
66
|
+
- _Template_ - Choose a built-in template (see [Examples](examples/README.md)), one you've added to the local templates directory, or - if a companion plugin like [`@rhizomatics/signalk-einklabel-genai-plugin`](templates.md#genai-rendering) is installed - one of its own contributed entries (shown with a suffix, e.g. "forecast (GenAI)")
|
|
67
|
+
- _Location/description_ - Optional free-text notes on where this label is physically mounted/viewed from, e.g. "chart table, viewed from ~1m in poor light" - available to any template as `source=label,path=description` (see [Label Details](templates.md#label-details))
|
|
68
|
+
- _Repaint Trigger_- Do you want this to repaint every few hours (at a chosen minutes past hour), or when a SignalK path changes?
|
|
69
|
+
- If it's a SignalK path, enter it next, for example `environment.tide.state`
|
|
70
|
+
- If it's 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.
|
|
71
|
+
|
|
72
|
+
The rest are grouped under _Advanced settings_, and can usually be ignored.
|
|
73
|
+
|
|
74
|
+
- _If the render doesn't match the panel size_ - see [Reframing](templates.md#reframing)
|
|
75
|
+
- _Compress upload_, _Mirror_ and _Wire format_ - see [Other Image Options](templates.md#other-image-options)
|
|
76
|
+
- _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.)
|
|
77
|
+
- _BLE AES key_ - Only needed if the default key doesn't work and you have a better alternative, otherwise ignore
|
|
78
|
+
- _Paint connect timeout_ and _Paint retries_ for this device - leave blank to use the plugin-wide settings, or set them for a label that's slower to respond or further away than the others
|
|
79
|
+
|
|
80
|
+
Configs saved by earlier versions are moved into this layout automatically when the plugin starts.
|
|
81
|
+
|
|
82
|
+
When the plugin starts, it will automatically re-paint the label if it's new, or the last timed slot was missed and the data has changed.
|
|
83
|
+
|
|
84
|
+
### Scanning for Devices
|
|
85
|
+
|
|
86
|
+
Since these are ultra-low power devices, they don't respond instantly to either identify themselves or accept a new image. By default, both scanning and painting have time-outs to wait for a response, which can be altered in the plugin configuration or CLI argument.
|
|
87
|
+
|
|
88
|
+
One other quirk is that some devices respond with a different name at different times, for example the generic `WOESL` sometimes and model specific `WL17500C74` other times. However, the MAC address, e.g. `66:66:17:50:0D:2B` is constant, and this is what's tracked by the plugin.
|
|
89
|
+
|
|
90
|
+
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 [Troubleshooting](faq.md#i-cant-see-my-device-as-a-choice-on-the-drop-down-list-after-scan).
|
|
91
|
+
|
|
92
|
+
## Supported Labels
|
|
93
|
+
|
|
94
|
+
### Zhsunyco
|
|
95
|
+
|
|
96
|
+
Also known as 'Suny' and 'WOLink'.
|
|
97
|
+
|
|
98
|
+
- [BLE ESLs](https://www.zhsunyco.com/digital-display-solution-for-small-retail-business/ble-esl-solution/)
|
|
99
|
+
- The range of labels available on retail sites like AliExpress may be larger than on their corporate site
|
|
100
|
+
- In mid 2026, a 4 colour (BWRY) 3.7" label retailed for about $35, with quantity discounts for bulk sets
|
|
101
|
+
- Cheapest units are 2 colour 1.54", and they go up to 7.5"
|
|
102
|
+
|
|
103
|
+
Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
|
|
104
|
+
|
|
105
|
+
### Gicisky
|
|
106
|
+
|
|
107
|
+
Known by other names, e.g. 'Picksmart', and with white label brands
|
|
108
|
+
|
|
109
|
+
- BLE ESLs
|
|
110
|
+
- Official store is on [AliExpress](https://www.aliexpress.com/store/911771479/pages/all-items.html?productGroupId=40000001654819&spm=a2g0o.store_pc_home.pcShopHead_6000727597996.1_1)
|
|
111
|
+
- Cheapest labels under £10 GBP / $13 USD
|