@rhizomatics/signalk-einklabel-plugin 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +25 -3
- package/README.md +72 -43
- package/dist/cli/index.js +4 -2
- package/dist/devices/bleDiscovery.js +20 -1
- package/dist/render/formatters.js +2 -0
- package/docs/assets/screenshots/example_watch_schedule.png +0 -0
- package/docs/assets/screenshots/label_config.png +0 -0
- package/package.json +5 -2
- package/templates/blank/250x128-BWRY.svg +69 -0
- package/templates/blank/416x240-BWRY.svg +86 -0
- package/templates/watch/416x240-BWRY.svg +199 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
|
+
# 0.10.1
|
|
2
|
+
|
|
3
|
+
## Watch Schedule Example
|
|
4
|
+
- New template to show current and next crew watch, using `signalk-watch-schedule` plugin
|
|
5
|
+
|
|
6
|
+
## CLI / Debugging
|
|
7
|
+
- `fields` command now reports where text field definitions can't be interpreted
|
|
8
|
+
- Epoch style timestamps now automatically handled as if proper date time for format options
|
|
9
|
+
|
|
10
|
+
# 0.9.1
|
|
11
|
+
|
|
12
|
+
- Fix for upstream EventEmitter leak in `@naugehyde/node-ble`
|
|
13
|
+
|
|
1
14
|
# 0.9.0
|
|
2
15
|
|
|
3
16
|
## Tide Clock Example
|
|
17
|
+
|
|
4
18
|
- Rename `third_quarter` to `last_quarter` for moon phase icons in `templates/.assets/lunar_phases` to match Derived Data plugin
|
|
5
19
|
- `tide.svg` renamed to `tides\416x240-BWRY.svg`. Original maintained but labelled as deprecated
|
|
6
|
-
- Added a simpler
|
|
20
|
+
- Added a simpler _250x128_ version of tide clock for 2.13" labels
|
|
21
|
+
|
|
7
22
|
## Fixes
|
|
23
|
+
|
|
8
24
|
- Fix configuration JSON holding older versions of itself as subentries
|
|
9
|
-
and data changes
|
|
25
|
+
and data changes
|
|
10
26
|
- Fix most cases of scanned devices not appearing in device dropdown choice
|
|
27
|
+
|
|
11
28
|
## Improvements
|
|
12
|
-
|
|
29
|
+
|
|
30
|
+
- Repaint state used to track which labels to repaint separately tracks the template
|
|
13
31
|
- `nearestColour` algorithm in Zhsunyco driver supports ESLs that only have BWR or BW
|
|
32
|
+
|
|
14
33
|
## Device Selection
|
|
34
|
+
|
|
15
35
|
- 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
36
|
- Scan and explicit device selection only required for boats with multiple ESL devices
|
|
37
|
+
|
|
17
38
|
## Templating
|
|
39
|
+
|
|
18
40
|
- Now support a directory of templates, where each is named like `416-240-BWRY.svg` to support same functions on different devices.
|
|
19
41
|
- 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
42
|
- `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,48 @@ 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
|
+
### Watch Schedule
|
|
86
98
|
|
|
87
|
-
|
|
99
|
+

|
|
88
100
|
|
|
89
|
-
|
|
101
|
+
Template available as 416x240-BWRY for 3.7" ESLs.
|
|
102
|
+
|
|
103
|
+
#### Pre-requisites
|
|
104
|
+
|
|
105
|
+
- Source of `watch.current` and `watch.next` values
|
|
106
|
+
- `signalk-watch-schedule` plugin
|
|
107
|
+
|
|
108
|
+
## Setting up a Label
|
|
109
|
+
|
|
110
|
+
Enable the plugin, and use the large **+** sign to add a label, which open up these fields.
|
|
111
|
+
|
|
112
|
+

|
|
113
|
+
|
|
114
|
+
- _Friendly Name_ - Give the label any name (word or phrase) you like, for example 'Tide Clock'
|
|
115
|
+
- _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.
|
|
116
|
+
- _Template_ - Choose a built in template, or one you've added to the local templates directory
|
|
117
|
+
- _Repaint Trigger_- Do you want this to repaint every few hours (at a chosen minutes past hour), or when a SignalK path changes?
|
|
118
|
+
- If its a SignalK path, enter it next, for example `environment.tide.state`
|
|
119
|
+
- 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.
|
|
120
|
+
|
|
121
|
+
There are also two more advanced options, which can usually be ignored.
|
|
122
|
+
|
|
123
|
+
- _BLE AES key_ - Only needed if the default key doesn't work and you have a better alternative, otherwise ignore
|
|
124
|
+
- _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.)
|
|
125
|
+
|
|
126
|
+
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
127
|
|
|
91
128
|
### Scanning for Devices
|
|
92
129
|
|
|
@@ -96,21 +133,18 @@ One other quirk is that some devices respond with a different name at different
|
|
|
96
133
|
|
|
97
134
|
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
135
|
|
|
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:
|
|
136
|
+
## Vendors
|
|
106
137
|
|
|
107
|
-
|
|
138
|
+
### Zhsunyco
|
|
108
139
|
|
|
109
|
-
|
|
140
|
+
Also known as 'Suny' and 'WOLink'.
|
|
110
141
|
|
|
111
|
-
|
|
142
|
+
- [BLE ESLs](https://www.zhsunyco.com/digital-display-solution-for-small-retail-business/ble-esl-solution/)
|
|
143
|
+
- The range of labels available on retail sites like AliExpress may be larger than on their corporate site
|
|
144
|
+
- In mid 2026, a 4 colour (BWRY) 3.7" label retailed for about $35, with quantity discounts for bulk sets
|
|
145
|
+
- Cheapest units are 2 colour 1.54", and they go up to 7.5"
|
|
112
146
|
|
|
113
|
-
|
|
147
|
+
Python code for a variety of their labels at https://github.com/roxburghm/zhsunyco-esl and https://github.com/NickWaterton/Wolink
|
|
114
148
|
|
|
115
149
|
## Templating
|
|
116
150
|
|
|
@@ -118,7 +152,9 @@ Templates are simply SVG files, to which expressions can be added to use SignalK
|
|
|
118
152
|
|
|
119
153
|
### Template Families (multiple panel sizes/colours)
|
|
120
154
|
|
|
121
|
-
A "Template" selection can either be one specific `.svg` file, or a _directory_ holding several same
|
|
155
|
+
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.
|
|
156
|
+
|
|
157
|
+
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
158
|
|
|
123
159
|
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
160
|
|
|
@@ -140,7 +176,7 @@ The source can be overridden to use the SignalK server's Resources API instead.
|
|
|
140
176
|
|
|
141
177
|
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
178
|
|
|
143
|
-
#### Plugin Data
|
|
179
|
+
#### Plugin Derived Data
|
|
144
180
|
|
|
145
181
|
`source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Available paths:
|
|
146
182
|
|
|
@@ -163,7 +199,7 @@ SignalK's unit preferences are used to automatically convert a `signalk`-sourced
|
|
|
163
199
|
|
|
164
200
|
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
201
|
|
|
166
|
-
Common categories
|
|
202
|
+
##### Common categories
|
|
167
203
|
|
|
168
204
|
- `depth` - Use the SignalK preferred depth unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
169
205
|
- `speed` - Use the SignalK preferred speed unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
@@ -183,7 +219,7 @@ path=environment.moon.phaseName,assets=lunar_phases
|
|
|
183
219
|
|
|
184
220
|
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
221
|
|
|
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.
|
|
222
|
+
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
223
|
|
|
188
224
|
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
225
|
|
|
@@ -195,19 +231,6 @@ Three font types are loaded by default, use the generic font family, or exact fo
|
|
|
195
231
|
- `sans-serif` - `Roboto`
|
|
196
232
|
- `monospace` - `Roboto Mono`
|
|
197
233
|
|
|
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
234
|
## Architecture
|
|
212
235
|
|
|
213
236
|
The primary things managed and provided by the plugin are:
|
|
@@ -271,7 +294,9 @@ Placeholder text isn't necessary, and is ignored by the plugin, but makes it muc
|
|
|
271
294
|
|
|
272
295
|
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
296
|
|
|
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`.
|
|
297
|
+
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`.
|
|
298
|
+
|
|
299
|
+
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
300
|
|
|
276
301
|
### Debugging Templates
|
|
277
302
|
|
|
@@ -351,6 +376,10 @@ Try a BLE proxy device, ESP32 is popular for this.
|
|
|
351
376
|
|
|
352
377
|
If you have an SVG viewer extension, this wll show the image rather than allowing editing of text. To solve, right click on the file in VSCode _Explorer_ view and choose to edit with _Text Editor_.
|
|
353
378
|
|
|
379
|
+
### Description is set in InkScape but doesn't render
|
|
380
|
+
|
|
381
|
+
Check if the text boxes are normal text or flowed text, and correct to normal text.
|
|
382
|
+
|
|
354
383
|
## Other ESL and General eInk Resources
|
|
355
384
|
|
|
356
385
|
- [Open ePaper Link](https://openepaperlink.de) - Alternative open source firmware to flash onto eInk shelf labels, with Home Assistant integration.
|
package/dist/cli/index.js
CHANGED
|
@@ -248,10 +248,12 @@ program
|
|
|
248
248
|
const elements = doc.getElementsByTagName(tag);
|
|
249
249
|
for (let i = 0; i < elements.length; i++) {
|
|
250
250
|
const element = elements.item(i);
|
|
251
|
-
|
|
252
|
-
if (!element || !desc?.textContent)
|
|
251
|
+
if (!element)
|
|
253
252
|
continue;
|
|
254
253
|
const id = element.getAttribute("id") ?? `${tag}#${i}`;
|
|
254
|
+
const desc = element.getElementsByTagName("desc").item(0);
|
|
255
|
+
if (!desc?.textContent)
|
|
256
|
+
continue;
|
|
255
257
|
try {
|
|
256
258
|
rows.push({ id, tag, desc: desc.textContent, binding: (0, binding_1.parseBinding)(desc.textContent) });
|
|
257
259
|
}
|
|
@@ -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`);
|
|
@@ -57,6 +57,8 @@ function parseTimestamp(value) {
|
|
|
57
57
|
return luxon_1.DateTime.fromJSDate(value, { zone: "utc" });
|
|
58
58
|
if (typeof value === "string")
|
|
59
59
|
return luxon_1.DateTime.fromISO(value, { zone: "utc" });
|
|
60
|
+
if (typeof value === "number")
|
|
61
|
+
return luxon_1.DateTime.fromMillis(value, { zone: "utc" });
|
|
60
62
|
return undefined;
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
Binary file
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhizomatics/signalk-einklabel-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Display SignalK data on eInk Electronic Shelf Labels",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ble",
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
"eink",
|
|
9
9
|
"esl",
|
|
10
10
|
"instrument",
|
|
11
|
+
"watch-schedule",
|
|
11
12
|
"signalk",
|
|
12
13
|
"signalk-category-hardware",
|
|
13
14
|
"signalk-category-instruments",
|
|
@@ -79,7 +80,9 @@
|
|
|
79
80
|
"appIcon": "docs/assets/icons/icon_tmp.png",
|
|
80
81
|
"screenshots": [
|
|
81
82
|
"docs/assets/screenshots/example_tidal_clock.png",
|
|
82
|
-
"docs/assets/screenshots/
|
|
83
|
+
"docs/assets/screenshots/example_watch_schedule.png",
|
|
84
|
+
"docs/assets/screenshots/plugin_config.png",
|
|
85
|
+
"docs/assets/screenshots/label_config.png"
|
|
83
86
|
]
|
|
84
87
|
}
|
|
85
88
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="250"
|
|
4
|
+
height="128"
|
|
5
|
+
viewBox="0 0 250 128"
|
|
6
|
+
version="1.1"
|
|
7
|
+
id="svg1"
|
|
8
|
+
sodipodi:docname="250x128-BWRY.svg"
|
|
9
|
+
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
|
10
|
+
xml:space="preserve"
|
|
11
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
12
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
|
15
|
+
id="defs1" /><sodipodi:namedview
|
|
16
|
+
id="namedview1"
|
|
17
|
+
pagecolor="#ffffff"
|
|
18
|
+
bordercolor="#000000"
|
|
19
|
+
borderopacity="0.25"
|
|
20
|
+
inkscape:showpageshadow="2"
|
|
21
|
+
inkscape:pageopacity="0.0"
|
|
22
|
+
inkscape:pagecheckerboard="0"
|
|
23
|
+
inkscape:deskcolor="#d1d1d1"
|
|
24
|
+
inkscape:zoom="1.3906433"
|
|
25
|
+
inkscape:cx="69.751891"
|
|
26
|
+
inkscape:cy="106.78511"
|
|
27
|
+
inkscape:window-width="1488"
|
|
28
|
+
inkscape:window-height="674"
|
|
29
|
+
inkscape:window-x="24"
|
|
30
|
+
inkscape:window-y="33"
|
|
31
|
+
inkscape:window-maximized="0"
|
|
32
|
+
inkscape:current-layer="svg1"
|
|
33
|
+
showgrid="true"
|
|
34
|
+
showguides="true"><inkscape:grid
|
|
35
|
+
id="grid1"
|
|
36
|
+
units="px"
|
|
37
|
+
originx="0"
|
|
38
|
+
originy="0"
|
|
39
|
+
spacingx="1"
|
|
40
|
+
spacingy="1"
|
|
41
|
+
empcolor="#0099e5"
|
|
42
|
+
empopacity="0.30196078"
|
|
43
|
+
color="#0099e5"
|
|
44
|
+
opacity="0.14901961"
|
|
45
|
+
empspacing="5"
|
|
46
|
+
dotted="false"
|
|
47
|
+
gridanglex="30"
|
|
48
|
+
gridanglez="30"
|
|
49
|
+
visible="true" /></sodipodi:namedview><rect
|
|
50
|
+
x="1.0169492"
|
|
51
|
+
y="2.0798673e-08"
|
|
52
|
+
width="249.17073"
|
|
53
|
+
height="129.97897"
|
|
54
|
+
fill="#ffffff"
|
|
55
|
+
id="rect1"
|
|
56
|
+
style="stroke-width:0.569552" /><text
|
|
57
|
+
id="last_repaint"
|
|
58
|
+
x="221.55899"
|
|
59
|
+
y="104.94675"
|
|
60
|
+
font-size="18"
|
|
61
|
+
font-family="serif"
|
|
62
|
+
fill="black"
|
|
63
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:10.6667px;font-family:serif;-inkscape-font-specification:'serif, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#ffff00;stroke-width:1.19309"
|
|
64
|
+
transform="scale(0.85412559,1.170788)"><desc
|
|
65
|
+
id="desc12">source=einklabel,path=repainted,format=local_datetime_short</desc><tspan
|
|
66
|
+
sodipodi:role="line"
|
|
67
|
+
id="tspan3"
|
|
68
|
+
x="221.55899"
|
|
69
|
+
y="104.94675">27 Jun 26 18:05</tspan></text></svg>
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="416"
|
|
4
|
+
height="240"
|
|
5
|
+
viewBox="0 0 416 240"
|
|
6
|
+
version="1.1"
|
|
7
|
+
id="svg1"
|
|
8
|
+
sodipodi:docname="416x240-BWRY.svg"
|
|
9
|
+
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
|
10
|
+
xml:space="preserve"
|
|
11
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
12
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
|
15
|
+
id="defs1" /><sodipodi:namedview
|
|
16
|
+
id="namedview1"
|
|
17
|
+
pagecolor="#ffffff"
|
|
18
|
+
bordercolor="#000000"
|
|
19
|
+
borderopacity="0.25"
|
|
20
|
+
inkscape:showpageshadow="2"
|
|
21
|
+
inkscape:pageopacity="0.0"
|
|
22
|
+
inkscape:pagecheckerboard="0"
|
|
23
|
+
inkscape:deskcolor="#d1d1d1"
|
|
24
|
+
inkscape:zoom="1.3906433"
|
|
25
|
+
inkscape:cx="155.3238"
|
|
26
|
+
inkscape:cy="187.32338"
|
|
27
|
+
inkscape:window-width="1488"
|
|
28
|
+
inkscape:window-height="674"
|
|
29
|
+
inkscape:window-x="24"
|
|
30
|
+
inkscape:window-y="33"
|
|
31
|
+
inkscape:window-maximized="0"
|
|
32
|
+
inkscape:current-layer="svg1"
|
|
33
|
+
showgrid="true"
|
|
34
|
+
showguides="true"><inkscape:grid
|
|
35
|
+
id="grid1"
|
|
36
|
+
units="px"
|
|
37
|
+
originx="0"
|
|
38
|
+
originy="0"
|
|
39
|
+
spacingx="1"
|
|
40
|
+
spacingy="1"
|
|
41
|
+
empcolor="#0099e5"
|
|
42
|
+
empopacity="0.30196078"
|
|
43
|
+
color="#0099e5"
|
|
44
|
+
opacity="0.14901961"
|
|
45
|
+
empspacing="5"
|
|
46
|
+
dotted="false"
|
|
47
|
+
gridanglex="30"
|
|
48
|
+
gridanglez="30"
|
|
49
|
+
visible="true" /></sodipodi:namedview><rect
|
|
50
|
+
x="1.0169492"
|
|
51
|
+
y="2.0798673e-08"
|
|
52
|
+
width="416"
|
|
53
|
+
height="240"
|
|
54
|
+
fill="#ffffff"
|
|
55
|
+
id="rect1" /><rect
|
|
56
|
+
style="fill:#ffff00;stroke:#000000;stroke-width:0.614045"
|
|
57
|
+
id="rect2"
|
|
58
|
+
width="416.94916"
|
|
59
|
+
height="18.410578"
|
|
60
|
+
x="-24.928873"
|
|
61
|
+
y="-242.40596"
|
|
62
|
+
transform="matrix(1,0,-0.1068988,-0.99426991,0,0)" /><text
|
|
63
|
+
id="last_repaint"
|
|
64
|
+
x="4.3477793"
|
|
65
|
+
y="201.9895"
|
|
66
|
+
font-size="18"
|
|
67
|
+
font-family="serif"
|
|
68
|
+
fill="black"
|
|
69
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:sans-serif;-inkscape-font-specification:'Roboto',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.19309"
|
|
70
|
+
transform="scale(0.85412558,1.170788)"><desc
|
|
71
|
+
id="desc12">source=einklabel,path=repainted,format=local_datetime_short</desc><tspan
|
|
72
|
+
sodipodi:role="line"
|
|
73
|
+
id="tspan3">27 Jun 26 18:05</tspan></text><text
|
|
74
|
+
id="plugin_version"
|
|
75
|
+
x="194.07487"
|
|
76
|
+
y="204.51942"
|
|
77
|
+
font-size="10.373px"
|
|
78
|
+
font-family="sans-serif"
|
|
79
|
+
fill="#ffff00"
|
|
80
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;direction:rtl;fill:#000000;stroke-width:0.864419"
|
|
81
|
+
transform="scale(0.86441854,1.156847)"><desc
|
|
82
|
+
id="desc16">source=einklabel,path=plugin_version</desc><tspan
|
|
83
|
+
sodipodi:role="line"
|
|
84
|
+
id="tspan1"
|
|
85
|
+
x="194.07487"
|
|
86
|
+
y="204.51942">v0.0.1</tspan></text></svg>
|
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="416"
|
|
4
|
+
height="240"
|
|
5
|
+
viewBox="0 0 416 240"
|
|
6
|
+
version="1.1"
|
|
7
|
+
id="svg1"
|
|
8
|
+
sodipodi:docname="416x240-BWRY.svg"
|
|
9
|
+
inkscape:version="1.3.2 (091e20e, 2023-11-25)"
|
|
10
|
+
xml:space="preserve"
|
|
11
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
12
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
13
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
14
|
+
xmlns:svg="http://www.w3.org/2000/svg"><defs
|
|
15
|
+
id="defs1"><rect
|
|
16
|
+
x="176.17746"
|
|
17
|
+
y="50.336416"
|
|
18
|
+
width="168.98654"
|
|
19
|
+
height="53.931875"
|
|
20
|
+
id="rect8" /><rect
|
|
21
|
+
x="53.931875"
|
|
22
|
+
y="29.482758"
|
|
23
|
+
width="134.47014"
|
|
24
|
+
height="40.988225"
|
|
25
|
+
id="rect4" /><rect
|
|
26
|
+
x="176.17746"
|
|
27
|
+
y="50.336414"
|
|
28
|
+
width="168.98654"
|
|
29
|
+
height="53.931873"
|
|
30
|
+
id="rect8-3" /><rect
|
|
31
|
+
x="176.17746"
|
|
32
|
+
y="50.336414"
|
|
33
|
+
width="168.98654"
|
|
34
|
+
height="53.931873"
|
|
35
|
+
id="rect8-3-5" /><rect
|
|
36
|
+
x="176.17746"
|
|
37
|
+
y="50.336414"
|
|
38
|
+
width="168.98654"
|
|
39
|
+
height="53.931873"
|
|
40
|
+
id="rect8-3-0" /><rect
|
|
41
|
+
x="176.17746"
|
|
42
|
+
y="50.336414"
|
|
43
|
+
width="168.98654"
|
|
44
|
+
height="53.931873"
|
|
45
|
+
id="rect8-3-5-4" /><rect
|
|
46
|
+
x="176.17746"
|
|
47
|
+
y="50.336414"
|
|
48
|
+
width="168.98654"
|
|
49
|
+
height="53.931873"
|
|
50
|
+
id="rect8-0" /><rect
|
|
51
|
+
x="53.931873"
|
|
52
|
+
y="29.482758"
|
|
53
|
+
width="134.47014"
|
|
54
|
+
height="40.988224"
|
|
55
|
+
id="rect4-3" /><rect
|
|
56
|
+
x="53.931873"
|
|
57
|
+
y="29.482758"
|
|
58
|
+
width="134.47014"
|
|
59
|
+
height="40.988224"
|
|
60
|
+
id="rect4-4" /><rect
|
|
61
|
+
x="176.17746"
|
|
62
|
+
y="50.336414"
|
|
63
|
+
width="168.98654"
|
|
64
|
+
height="53.931873"
|
|
65
|
+
id="rect8-0-4" /></defs><sodipodi:namedview
|
|
66
|
+
id="namedview1"
|
|
67
|
+
pagecolor="#ffffff"
|
|
68
|
+
bordercolor="#000000"
|
|
69
|
+
borderopacity="0.25"
|
|
70
|
+
inkscape:showpageshadow="2"
|
|
71
|
+
inkscape:pageopacity="0.0"
|
|
72
|
+
inkscape:pagecheckerboard="0"
|
|
73
|
+
inkscape:deskcolor="#d1d1d1"
|
|
74
|
+
inkscape:zoom="0.98333331"
|
|
75
|
+
inkscape:cx="210"
|
|
76
|
+
inkscape:cy="78.305087"
|
|
77
|
+
inkscape:window-width="1488"
|
|
78
|
+
inkscape:window-height="675"
|
|
79
|
+
inkscape:window-x="24"
|
|
80
|
+
inkscape:window-y="33"
|
|
81
|
+
inkscape:window-maximized="0"
|
|
82
|
+
inkscape:current-layer="svg1"
|
|
83
|
+
showgrid="true"
|
|
84
|
+
showguides="true"><inkscape:grid
|
|
85
|
+
id="grid1"
|
|
86
|
+
units="px"
|
|
87
|
+
originx="0"
|
|
88
|
+
originy="0"
|
|
89
|
+
spacingx="1"
|
|
90
|
+
spacingy="1"
|
|
91
|
+
empcolor="#0099e5"
|
|
92
|
+
empopacity="0.30196078"
|
|
93
|
+
color="#0099e5"
|
|
94
|
+
opacity="0.14901961"
|
|
95
|
+
empspacing="5"
|
|
96
|
+
dotted="false"
|
|
97
|
+
gridanglex="30"
|
|
98
|
+
gridanglez="30"
|
|
99
|
+
visible="true" /></sodipodi:namedview><rect
|
|
100
|
+
x="1.0169492"
|
|
101
|
+
y="2.0798673e-08"
|
|
102
|
+
width="416"
|
|
103
|
+
height="240"
|
|
104
|
+
fill="#ffffff"
|
|
105
|
+
id="rect1"
|
|
106
|
+
inkscape:label="label" /><rect
|
|
107
|
+
style="fill:#ffff00;stroke:#000000;stroke-width:0.614045"
|
|
108
|
+
id="rect2"
|
|
109
|
+
width="416.94916"
|
|
110
|
+
height="18.410578"
|
|
111
|
+
x="-24.928873"
|
|
112
|
+
y="-242.40596"
|
|
113
|
+
inkscape:label="bg_footer" /><text
|
|
114
|
+
id="last_repaint"
|
|
115
|
+
x="3.71355"
|
|
116
|
+
y="236.486883"
|
|
117
|
+
font-size="18"
|
|
118
|
+
font-family="serif"
|
|
119
|
+
fill="black"
|
|
120
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:sans-serif;-inkscape-font-specification:'Roboto',sans-serif;font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#000000;stroke-width:1.19309"><desc
|
|
121
|
+
id="desc12">source=einklabel,path=repainted,format=local_datetime_short</desc>27 Jun 26 18:05</text><text
|
|
122
|
+
id="plugin_version"
|
|
123
|
+
x="167.761916"
|
|
124
|
+
y="236.597677"
|
|
125
|
+
font-size="10.373px"
|
|
126
|
+
font-family="sans-serif"
|
|
127
|
+
fill="#ffff00"
|
|
128
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:Roboto;-inkscape-font-specification:'Roboto, Normal';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;fill:#000000;stroke-width:0.864419"><desc
|
|
129
|
+
id="desc16">source=einklabel,path=plugin_version</desc>v0.0.1</text><text
|
|
130
|
+
id="watch_system"
|
|
131
|
+
x="321.466726"
|
|
132
|
+
y="236.116556"
|
|
133
|
+
font-size="10.373px"
|
|
134
|
+
font-family="sans-serif"
|
|
135
|
+
fill="#ffff00"
|
|
136
|
+
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:13.3333px;font-family:serif;-inkscape-font-specification:'serif, @wght=100';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variation-settings:'wght' 100;fill:#000000;stroke-width:0.864419"><desc
|
|
137
|
+
id="desc7">watch.system.name</desc>2-on / 2-off</text><text
|
|
138
|
+
id="text4-6"
|
|
139
|
+
x="8.407525"
|
|
140
|
+
y="31.738578"
|
|
141
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:serif;-inkscape-font-specification:'serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;fill:#808080;stroke:none;stroke-width:0.612283;stroke-opacity:1"
|
|
142
|
+
inkscape:label="title">Watches</text><text
|
|
143
|
+
id="current_start"
|
|
144
|
+
x="7.3719606"
|
|
145
|
+
y="120.02129"
|
|
146
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;stroke:#ffff00;stroke-width:0.56247"><desc
|
|
147
|
+
id="desc1">path=watch.current.startTime,format=local_time</desc>12:00</text><text
|
|
148
|
+
id="current_end"
|
|
149
|
+
x="117.756"
|
|
150
|
+
y="120.04606"
|
|
151
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;stroke:#ffff00;stroke-width:0.612;stroke-dasharray:none"><desc
|
|
152
|
+
id="desc2">path=watch.current.endTime,format=local_time</desc>16:00</text><text
|
|
153
|
+
id="next_team"
|
|
154
|
+
x="219.44624"
|
|
155
|
+
y="205.76323"
|
|
156
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:29.3333px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;stroke:none;stroke-width:0.612283"><desc
|
|
157
|
+
id="desc5">watch.next.teamName</desc>Team 2</text><text
|
|
158
|
+
id="current_team"
|
|
159
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:32px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;fill-opacity:1;stroke:#ffff00;stroke-width:0.73;stroke-dasharray:none;stroke-opacity:1"
|
|
160
|
+
x="221.56224"
|
|
161
|
+
y="121.52837"><desc
|
|
162
|
+
id="desc5-6">watch.current.teamName</desc>Team 1</text><text
|
|
163
|
+
id="next_start"
|
|
164
|
+
x="6.5166879"
|
|
165
|
+
y="205.07277"
|
|
166
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;font-variant-east-asian:normal;fill:#1a1a1a;stroke:none;stroke-width:0.580975"
|
|
167
|
+
inkscape:label="next_start"><desc
|
|
168
|
+
id="desc4">path=watch.next.startTime,format=local_time</desc>16:00</text><text
|
|
169
|
+
id="next_end"
|
|
170
|
+
x="119.80927"
|
|
171
|
+
y="205.40628"
|
|
172
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;fill:#1a1a1a;stroke:none;stroke-width:0.612283"><desc
|
|
173
|
+
id="desc3">path=watch.next.endTime,format=local_time</desc>20:00</text>
|
|
174
|
+
|
|
175
|
+
<rect
|
|
176
|
+
style="fill:#ff0000;stroke:none;stroke-width:2.621;stroke-dasharray:none;stroke-opacity:1"
|
|
177
|
+
id="rect15"
|
|
178
|
+
width="397.62714"
|
|
179
|
+
height="35.59322"
|
|
180
|
+
x="9.1525431"
|
|
181
|
+
y="52.881355"
|
|
182
|
+
inkscape:label="bg_current" /><text
|
|
183
|
+
id="text4"
|
|
184
|
+
x="20.708879"
|
|
185
|
+
y="80.673174"
|
|
186
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;fill:#f9f9f9;stroke:#fefe54;stroke-width:0.612283"
|
|
187
|
+
inkscape:label="label_current">Current</text><rect
|
|
188
|
+
style="fill:#666666;stroke:none;stroke-width:2.6277;stroke-dasharray:none;stroke-opacity:1"
|
|
189
|
+
id="rect15-7"
|
|
190
|
+
width="399.66104"
|
|
191
|
+
height="35.59322"
|
|
192
|
+
x="8.5084782"
|
|
193
|
+
y="140.50848"
|
|
194
|
+
inkscape:label="bg_next" /><text
|
|
195
|
+
id="text4-7"
|
|
196
|
+
x="22.098717"
|
|
197
|
+
y="167.28336"
|
|
198
|
+
style="font-style:normal;font-variant:normal;font-weight:bold;font-stretch:normal;font-size:26.6667px;font-family:sans-serif;-inkscape-font-specification:'sans-serif, Bold';font-variant-ligatures:normal;font-variant-caps:normal;font-variant-numeric:normal;fill:#f9f9f9;stroke:#fefe54;stroke-width:0.612283"
|
|
199
|
+
inkscape:label="label_next">Next</text></svg>
|