@rhizomatics/signalk-einklabel-plugin 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +48 -3
- package/LICENSE +201 -0
- package/README.md +63 -43
- package/dist/cli/index.js +39 -12
- package/dist/config.js +7 -1
- package/dist/devices/zhsunyco/index.js +2 -4
- package/dist/devices/zhsunyco/protocol.js +1 -4
- package/package.json +10 -5
- package/templates/tide.svg +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,47 +1,92 @@
|
|
|
1
|
+
# 0.6.2
|
|
2
|
+
|
|
3
|
+
- Auto default SignalK URL if `-u` or `-e` not passed as CLI arguments
|
|
4
|
+
- Default timeout for painting a device screen is now 60 seconds
|
|
5
|
+
- Code now prettified, and prettier check added to CI
|
|
6
|
+
- Test coverage improved and now reported
|
|
7
|
+
|
|
8
|
+
# 0.6.1
|
|
9
|
+
|
|
10
|
+
- Corrected tide lunar phase path from `environment.moonPhase.name` to `environment.moon.phaseName`
|
|
11
|
+
|
|
1
12
|
# 0.6.0
|
|
2
|
-
|
|
13
|
+
|
|
14
|
+
- Added ability to select images for inclusion in SVG templates based on SignalK path value
|
|
15
|
+
- Pass a directory of assets, and it will pick the SVG file matching the SignalK value
|
|
16
|
+
- Matching will cope with "Waning Gibbous" -> `waning_gibbous.svg`
|
|
17
|
+
- If no match, image will be blank
|
|
3
18
|
- New image selection used to add lunar phase to tide clock example
|
|
4
|
-
-
|
|
19
|
+
- Requires a source for `environment.moon.phaseName`, for example the `dervived-data` plugin
|
|
20
|
+
- New `resources` directory for composable SVG assets
|
|
21
|
+
|
|
5
22
|
# 0.5.1
|
|
23
|
+
|
|
6
24
|
- Work around `resvg-wasm` font limitations by overriding generic font family with matching font name prior to rendering
|
|
25
|
+
|
|
7
26
|
# 0.5.0
|
|
27
|
+
|
|
8
28
|
## Font Handling
|
|
29
|
+
|
|
9
30
|
- Update set of built-in fonts for consistent Roboto monospace and serif
|
|
10
31
|
- Update `tide.svg` template to use explicit font names to work around `resvg-wasm` limitations
|
|
32
|
+
|
|
11
33
|
## Offline Design
|
|
34
|
+
|
|
12
35
|
- Offline template development now possible using example data,
|
|
13
36
|
- Pass `-e` or `--example-data` as a CLI argument, pointing to a set of example JSON files
|
|
14
37
|
- Examples of example data provided in plugin and at https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/examples
|
|
15
38
|
- Works with `render`,`paint`,`field` and `fields`
|
|
39
|
+
|
|
16
40
|
# 0.4.7
|
|
41
|
+
|
|
17
42
|
- Fix node-ble imports
|
|
43
|
+
|
|
18
44
|
# 0.4.6
|
|
45
|
+
|
|
19
46
|
- Correct `node-ble` dependency to `@naugehyde/node-ble`
|
|
47
|
+
|
|
20
48
|
# 0.4.5
|
|
49
|
+
|
|
21
50
|
- Updated `tide.svg` layout
|
|
22
51
|
- Added SignalK standard Github Actions workflow
|
|
52
|
+
|
|
23
53
|
# 0.4.4
|
|
54
|
+
|
|
24
55
|
- Update `node-ble` to new packaging
|
|
56
|
+
|
|
25
57
|
# 0.4.3
|
|
58
|
+
|
|
26
59
|
- Fixed datetime values when running from plugin were blank while CLI was fine
|
|
27
60
|
- Added an example Resources API output from signalk-tides plugin
|
|
28
61
|
- Relaid out the example tide clock, adding the tidal range (LAT to HAT) and the source of tide data
|
|
29
62
|
- Alternative source of time zone info, `source=einklabel,path=local_zone`
|
|
63
|
+
|
|
30
64
|
# 0.4.2
|
|
65
|
+
|
|
31
66
|
- Fix High/Low display for tide clock template
|
|
32
67
|
- Fix last repaint time for display being hour out
|
|
33
68
|
- Change default name of template directory to 'einklabel/templates'
|
|
69
|
+
|
|
34
70
|
# 0.4.1
|
|
71
|
+
|
|
35
72
|
- Packaging fixes
|
|
73
|
+
|
|
36
74
|
# 0.4.0
|
|
75
|
+
|
|
37
76
|
- Added `einklabel` as a `source` for template fields, and `repainted` as path
|
|
38
77
|
- Added `local_datetime_short` as a datetime format option, for `27 Jun 26 18:05` style output
|
|
39
78
|
- Add Last Repainted field to the Tide Clock example
|
|
79
|
+
|
|
40
80
|
# 0.3.2
|
|
81
|
+
|
|
41
82
|
- Renamed to signalk-einklabel-plugin
|
|
42
83
|
- Added core test suite
|
|
84
|
+
|
|
43
85
|
# 0.3.1
|
|
86
|
+
|
|
44
87
|
- Correct name, and include changelog
|
|
88
|
+
|
|
45
89
|
# 0.3.0
|
|
90
|
+
|
|
46
91
|
- First published beta release
|
|
47
|
-
- Tested publishing Tide Clock on interval to a Zhunyco 3.7" display
|
|
92
|
+
- Tested publishing Tide Clock on interval to a Zhunyco 3.7" display
|
package/LICENSE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/README.md
CHANGED
|
@@ -1,41 +1,58 @@
|
|
|
1
1
|
# eInk Labels for SignalK
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/@rhizomatics/signalk-einklabel-plugin)
|
|
4
|
+
[](https://www.npmjs.com/package/@rhizomatics/signalk-einklabel-plugin)
|
|
5
|
+
[](https://github.com/rhizomatics/signalk-einklabel-plugin/actions/workflows/signalk-ci.yml)
|
|
6
|
+
[](https://codecov.io/gh/rhizomatics/signalk-einklabel-plugin)
|
|
7
|
+
[](https://github.com/prettier/prettier)
|
|
8
|
+
[](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/LICENSE)
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
** Fully working but limited vendor/product support **
|
|
6
11
|
|
|
7
|
-
|
|
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.
|
|
13
|
+
|
|
14
|
+
## What is an ESL?
|
|
15
|
+
|
|
16
|
+
Electronic Shelf Labels are [eInk](https://en.wikipedia.org/wiki/E_Ink) devices that consume very little battery energy, presuming they are not constantly updated - the battery is used only when the display changes, and a periodic BLE check for incoming changes. Perfect for info that changes only once or twice a day, like tidal information.
|
|
8
17
|
|
|
9
18
|
Since they are designed to be used in large quantity in small shops, they are cheap and simple devices. Earlier models required dedicated controllers, or updates over Wifi or NFC, whereas many modern ones are standalone BLE devices that can be updated from a phone or server.
|
|
10
19
|
|
|
11
|
-
|
|
20
|
+
Being battery operated, they can be stuck on anywhere without wiring - the only constraints are bluetooth range, visibility (they need ambient light since the display is more like paper than a traditional lit-up electronic display) and out of the weather since the devices are intended for indoor use.
|
|
12
21
|
|
|
13
22
|
## Pre-requisites
|
|
14
23
|
|
|
15
|
-
|
|
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
|
+
|
|
26
|
+
Most of these requirements are about making SignalK work with Bluetooth Low Energy, which is good thing to have anyway, since vendors like Victron, Switchbot, Ruuvi and others have BLE enabled hardware that's useful to have on a boat. [Direct BLE support](https://github.com/SignalK/signalk-server/issues/2411) in SignalK is being planned in 2026.
|
|
16
27
|
|
|
17
28
|
1. A SignalK server, preferably running Linux (MacOS does weird things with bluetooth)
|
|
18
29
|
2. A Bluetooth adapter, that can handle BLE (Bluetooth Low Energy), which is Bluetooth v4.0 or higher
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
30
|
+
|
|
31
|
+
- Bluetooth adapters for Linux can be tricky, TP-Link UB400 and Asus USB-BT500 are two well-known and available ones
|
|
32
|
+
- Some Raspberry Pi models come with suitable Bluetooth it built-in
|
|
33
|
+
- Don't worry about the very latest Bluetooth versions, 4.0 is basic, 5.0 is nice
|
|
34
|
+
- Home Assistant is massively more popular than SignalK, and often also run on Raspberry Pi and similar, so good source of advice
|
|
35
|
+
|
|
23
36
|
3. `bluez` package installed in Linux
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
|
|
38
|
+
- No need to do this if you have a Raspberry Pi with recent Raspian version, since bluez comes built in.
|
|
39
|
+
- If you're not running a Raspberry Pi, then ensure that the `dbus` package is installed
|
|
40
|
+
|
|
26
41
|
4. One or more supported Electronic Shelf Labels
|
|
27
|
-
|
|
42
|
+
|
|
43
|
+
- The label used for testing this is the [ZhunyCo 3.7 BRWY](https://www.aliexpress.com/item/1005010050104435.html)
|
|
44
|
+
|
|
28
45
|
5. Correct time zone set on server if local time is to be shown on display
|
|
29
|
-
- Use `raspi-config` on a Raspberry Pi, or `timedatectl` on a Linux server
|
|
30
|
-
- If not set, everything will work, but you may see the wrong zone or not have daylight savings applied
|
|
31
46
|
|
|
32
|
-
|
|
47
|
+
- Use `raspi-config` on a Raspberry Pi, or `timedatectl` on a Linux server
|
|
48
|
+
- If not set, everything will work, but you may see the wrong zone or not have daylight savings applied
|
|
33
49
|
|
|
50
|
+
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.
|
|
34
51
|
|
|
35
52
|
## Installation
|
|
36
53
|
|
|
37
54
|
Look for **eInk Label Instrument** in the [SignalK AppStore]() on your
|
|
38
|
-
server ( under
|
|
55
|
+
server ( under _Apps & Plugins_ on the latest version).
|
|
39
56
|
|
|
40
57
|
### Using Outside of SignalK
|
|
41
58
|
|
|
@@ -53,7 +70,7 @@ npm install @rhizomatics/signalk-einklabel-plugin
|
|
|
53
70
|
|
|
54
71
|
The tide clock needs the [signalk-tides](https://github.com/openwatersio/signalk-tides) plugin to be installed and publishing tides to the Resources API. The [tide.svg](https://github.com/rhizomatics/signalk-einklabel-plugin/blob/main/templates/tide.svg) can be customized to run with other APIs or take data only from SignalK data paths.
|
|
55
72
|
|
|
56
|
-
To show the lunar phase, the `environment.
|
|
73
|
+
To show the lunar phase, the `environment.moon.phaseName` path is required, which can
|
|
57
74
|
be easily achieved by installing and configuring the `derived-data` plugin.
|
|
58
75
|
|
|
59
76
|
## Templating
|
|
@@ -76,19 +93,19 @@ The source can be overridden to use the SignalK server's Resources API instead.
|
|
|
76
93
|
|
|
77
94
|
`source=einklabel` reads data injected by the plugin itself, rather than from SignalK. Available paths:
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
96
|
+
- `path=repainted` - the timestamp of the current repaint - for example `source=einklabel,path=repainted,format=local_datetime_short` to show when the label was last updated.
|
|
97
|
+
- `path=local_zone` - a short zone name (e.g. `BST`) for the same timezone used for `local_time`/`day_mon`/`local_datetime_short` (see above) - a fallback for `environment.time.timezoneRegion,format=utc_offset` on installs that never publish that path, since it needs no SignalK metadata of its own. Falls back to a plain UTC offset like `GMT+1` where the host's locale has no real abbreviation for the zone.
|
|
81
98
|
|
|
82
99
|
#### Customizing Output
|
|
83
100
|
|
|
84
101
|
A `format` can be specified to make the value easier to understand. The supported formats are:
|
|
85
102
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
103
|
+
- `local_time` - reduce a time stamp to just the time (H:M:S), omitting the date, and applying daylight savings if appropriate
|
|
104
|
+
- `day_mon` - reduce a time stamp to day and month, e.g. `27 Jun`, applying daylight savings if appropriate
|
|
105
|
+
- `local_datetime_short` - format a time stamp as day, abbreviated month, 2-digit year and 24h time, e.g. `21 Jun 26 18:05`, applying daylight savings if appropriate
|
|
106
|
+
- `utc_offset` - Show a timezone in `UTC+01:00` style format
|
|
107
|
+
- `position` - Format a `{ latitude, longitude }` value as decimal degrees with hemisphere letters, e.g. `56.6250°N 6.0700°W`
|
|
108
|
+
- `raw` - Don't apply automatic SignalK unit conversion and symbol display (see below)
|
|
92
109
|
|
|
93
110
|
SignalK's unit preferences are used to automatically convert a `signalk`-sourced numeric value to its preferred display unit, and append a unit symbol like `kt` or `m`, unless `format=raw` is specified to switch that off. However, when using plugin or API data there may be no path metadata to convert from (for example `signalk-tides` publishes tide data to the Resources API, and `level` is a raw metre value with no SignalK path of its own) - in these cases an explicit `category` can be given instead, and the unit preferences will be applied the same way, for example `category=depth` for the tides level figure.
|
|
94
111
|
|
|
@@ -96,9 +113,9 @@ Note that for dates and times, the server timezone must be set correctly, for ex
|
|
|
96
113
|
|
|
97
114
|
Common categories:
|
|
98
115
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
116
|
+
- `depth` - Use the SignalK preferred depth unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
117
|
+
- `speed` - Use the SignalK preferred speed unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
118
|
+
- `temperature` - Use the SignalK preferred temperature unit, make the conversion if needed, and tack on the unit name as a suffix
|
|
102
119
|
|
|
103
120
|
Additionally, `round=n` can be used to round to limited decimal places.
|
|
104
121
|
|
|
@@ -109,7 +126,7 @@ These can all be combined as in `source=resources,resource=tides,path=extremes[2
|
|
|
109
126
|
The same `<desc>` mechanism works on an `<image>` element instead of a `<text>` element, for a value that's better shown as a picture than as text - a moon phase icon, a wind direction arrow, a weather condition glyph, and so on. Rather than substituting text, the resolved value picks one of a directory of `.svg` files to embed, by an extra required `assets=` key naming that directory (resolved relative to the template file itself, so a bundled template and a user override both work the same way). For example, the tide clock's moon phase icon uses:
|
|
110
127
|
|
|
111
128
|
```
|
|
112
|
-
path=environment.
|
|
129
|
+
path=environment.moon.phaseName,assets=../resources/svg/lunar_phases
|
|
113
130
|
```
|
|
114
131
|
|
|
115
132
|
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 simply omitted from that render - no broken image, no placeholder, nothing shown.
|
|
@@ -120,9 +137,9 @@ This is a general mechanism, not specific to moon phases - any `source`/`context
|
|
|
120
137
|
|
|
121
138
|
Three font types are loaded by default, use the generic font family, or exact font name, in the SVG editor and choose size and weight (bold, semi-bold etc). Some labels will make a decent attempt to gray scale. Use the simple pure red, yellow, white, black to match the label's limited colour choice (some labels only offer black and white, or black/white/red). If a font can't be matched it will default to (sans-serif) Roboto.
|
|
122
139
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
140
|
+
- `serif` - `Roboto Serif`
|
|
141
|
+
- `sans-serif` - `Roboto`
|
|
142
|
+
- `monospace` - `Roboto Mono`
|
|
126
143
|
|
|
127
144
|
## Command Line Interface
|
|
128
145
|
|
|
@@ -132,12 +149,13 @@ To get fast feedback on templates and shelf devices without updating and configu
|
|
|
132
149
|
- `scan` - report supported devices found from a BLE scan
|
|
133
150
|
|
|
134
151
|
See also the commands useful for debugging under [Developing Templates]
|
|
152
|
+
|
|
135
153
|
- `render` - transform an SVG template and data into a PNG
|
|
136
154
|
- `paint` - render an SVG template and data to a selected ESL
|
|
137
155
|
|
|
138
156
|
The width, height, vertical offset and colour palette for the device is taken from the internal register of devices, however can be overridden on the command line. This could be used to help you choose what size of label to buy, or to get an unsupported label working.
|
|
139
157
|
|
|
140
|
-
( 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 `
|
|
158
|
+
( 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` )
|
|
141
159
|
|
|
142
160
|
## Vendors
|
|
143
161
|
|
|
@@ -156,12 +174,12 @@ Python code for a variety of their labels at https://github.com/roxburghm/zhsuny
|
|
|
156
174
|
|
|
157
175
|
The primary things managed and provided by the plugin are:
|
|
158
176
|
|
|
159
|
-
|
|
177
|
+
- ESL Vendor
|
|
160
178
|
- Sub-package per vendor
|
|
161
|
-
|
|
179
|
+
- ESL Device
|
|
162
180
|
- Metadata in the vendor package, using a `pid` or sometimes `pid` combined with `hwid` in the BLE results to pinpoint a model
|
|
163
|
-
|
|
164
|
-
|
|
181
|
+
- SVG Template
|
|
182
|
+
- SignalK API base URL
|
|
165
183
|
- 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
|
|
166
184
|
- 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
|
|
167
185
|
- 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
|
|
@@ -194,17 +212,17 @@ Due to a limitation in the `resvg-wasm` library used to turn SVGs into images, t
|
|
|
194
212
|
|
|
195
213
|
The `esl-cli` can be used to debug and validate templates quickly:
|
|
196
214
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
215
|
+
- `render` - Render templates with SignalK data and write to a local PNG file
|
|
216
|
+
- `paint` - Render templates with SignalK data and send to selected ESL device
|
|
217
|
+
- `fields` - List the fields in the template, with the source specification and the rendered data value
|
|
218
|
+
- `field` - Accept a source specification (outside of any template context) and return the rendered value if available
|
|
201
219
|
|
|
202
220
|
### Examples
|
|
203
221
|
|
|
204
222
|
#### List all Fields and Rendered Values
|
|
205
223
|
|
|
206
224
|
```bash
|
|
207
|
-
|
|
225
|
+
npx esl-cli fields -t templates/tide.svg -u http://localhost
|
|
208
226
|
```
|
|
209
227
|
|
|
210
228
|
```
|
|
@@ -235,4 +253,6 @@ extremes.0.level source=resources,resource=tides,path=extremes[0].level,
|
|
|
235
253
|
|
|
236
254
|
- `vessels.json` - The standard SignalK vessel paths
|
|
237
255
|
- `resources/xxxx.json` - The output of the `xxxx` resources API call
|
|
238
|
-
- `categories.json` - SignalK unit categories needed for `category=depth` type formatting
|
|
256
|
+
- `categories.json` - SignalK unit categories needed for `category=depth` type formatting
|
|
257
|
+
|
|
258
|
+
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.
|
package/dist/cli/index.js
CHANGED
|
@@ -11,10 +11,12 @@ const svgRenderer_1 = require("../render/svgRenderer");
|
|
|
11
11
|
const png_1 = require("../render/png");
|
|
12
12
|
const binding_1 = require("../render/binding");
|
|
13
13
|
const liveContext_1 = require("./liveContext");
|
|
14
|
+
const httpJson_1 = require("../httpJson");
|
|
14
15
|
const log_1 = require("./log");
|
|
15
16
|
(0, registry_1.registerDriver)(new zhsunyco_1.ZhsunycoDriver());
|
|
16
17
|
const VENDOR_IDENTIFY_TIMEOUT_MS = 30000;
|
|
17
|
-
|
|
18
|
+
/** Tried in order when -u/--url is omitted (and -e/--example-data isn't given) - first one that answers wins. */
|
|
19
|
+
const DEFAULT_SIGNALK_URLS = ['http://localhost', 'http://localhost:3000', 'https://localhost'];
|
|
18
20
|
const COLOUR_CODES = {
|
|
19
21
|
BW: ['black', 'white'],
|
|
20
22
|
BWR: ['black', 'white', 'red'],
|
|
@@ -27,9 +29,26 @@ function parseColours(code) {
|
|
|
27
29
|
}
|
|
28
30
|
return colours;
|
|
29
31
|
}
|
|
30
|
-
/**
|
|
31
|
-
function
|
|
32
|
-
|
|
32
|
+
/** Probes DEFAULT_SIGNALK_URLS in order and returns the first that answers a plain GET - used when -u/--url is omitted. */
|
|
33
|
+
async function resolveDefaultUrl() {
|
|
34
|
+
for (const candidate of DEFAULT_SIGNALK_URLS) {
|
|
35
|
+
try {
|
|
36
|
+
(0, log_1.logDebug)(`probing ${candidate} as a default SignalK server`);
|
|
37
|
+
await (0, httpJson_1.fetchJson)(`${candidate}/signalk`);
|
|
38
|
+
return candidate;
|
|
39
|
+
}
|
|
40
|
+
catch (err) {
|
|
41
|
+
(0, log_1.logDebug)(`${candidate} did not answer: ${err.message}`);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
throw new Error(`no -u/--url given and none of ${DEFAULT_SIGNALK_URLS.join(', ')} answered - specify the server explicitly with -u/--url`);
|
|
45
|
+
}
|
|
46
|
+
/** Shared by every command that takes -u/--url and -e/--example-data - -e wins when both are present; when neither is given, probes DEFAULT_SIGNALK_URLS for a default. */
|
|
47
|
+
async function assembleContext(opts, bindings) {
|
|
48
|
+
if (opts.exampleData)
|
|
49
|
+
return (0, liveContext_1.assembleExampleContext)(opts.exampleData, bindings);
|
|
50
|
+
const url = opts.url ?? (await resolveDefaultUrl());
|
|
51
|
+
return (0, liveContext_1.assembleLiveContext)(url, bindings);
|
|
33
52
|
}
|
|
34
53
|
/** Connects long enough to read the advertised name and manufacturer ID, then matches against registered drivers. */
|
|
35
54
|
async function identifyVendor(address) {
|
|
@@ -92,7 +111,7 @@ program
|
|
|
92
111
|
.command('scan')
|
|
93
112
|
.description('Scan for supported BLE ESL devices across all registered vendor drivers')
|
|
94
113
|
.option('-d, --duration <seconds>', 'scan duration in seconds', '10')
|
|
95
|
-
.option('-a, --all-devices',
|
|
114
|
+
.option('-a, --all-devices', "list every nearby BLE device, not just ones a registered driver recognised - unmatched devices show address/name/mfr/rssi only, since there's no driver to do a vendor-specific read like battery")
|
|
96
115
|
.action(async (opts) => {
|
|
97
116
|
const durationMs = Number(opts.duration) * 1000;
|
|
98
117
|
const header = ['vendor', 'address', 'name', 'pid', 'label', 'mfr', 'battery', 'rssi'];
|
|
@@ -137,12 +156,14 @@ program
|
|
|
137
156
|
program
|
|
138
157
|
.command('paint')
|
|
139
158
|
.description('Render a template against a live SignalK server and send it to a device')
|
|
140
|
-
.option('-v, --vendor <vendor>',
|
|
159
|
+
.option('-v, --vendor <vendor>', "vendor driver to use - if omitted, inferred from the device's advertised name")
|
|
141
160
|
.requiredOption('-a, --address <address>', 'BLE address of the device')
|
|
142
161
|
.requiredOption('-t, --template <path>', 'path to SVG template')
|
|
143
|
-
.option('-u, --url <url>',
|
|
162
|
+
.option('-u, --url <url>', "SignalK server base URL - resolves the template's source=signalk/resources bindings - if omitted, tries each of " +
|
|
163
|
+
DEFAULT_SIGNALK_URLS.join(', ') +
|
|
164
|
+
' in turn')
|
|
144
165
|
.option('-e, --example-data <dir>', 'load vessels/resources from local example JSON files in <dir> (e.g. ./examples) instead of a live SignalK server - alternative to -u')
|
|
145
|
-
.option('-k, --aes-key <hex>',
|
|
166
|
+
.option('-k, --aes-key <hex>', "AES-128 key for device authentication, as 32 hex characters - defaults to the vendor's stock key if omitted")
|
|
146
167
|
.option('-w, --width <px>', 'render width', '416')
|
|
147
168
|
.option('--height <px>', 'render height', '240')
|
|
148
169
|
.option('--voffset <px>', 'vertical pixel offset of the panel - overrides the looked-up model for unsupported hardware (requires --colours)', '0')
|
|
@@ -175,14 +196,16 @@ program
|
|
|
175
196
|
}
|
|
176
197
|
await driver.paint(bitmap, { address: opts.address, aesKey: opts.aesKey, modelOverride, connectTimeoutMs });
|
|
177
198
|
});
|
|
178
|
-
console.log(`painted ${opts.address} (${bitmap.width}x${bitmap.height})`);
|
|
199
|
+
console.log(`painted ${opts.address} (${bitmap.width}x${bitmap.height}) ${opts.colours}`);
|
|
179
200
|
});
|
|
180
201
|
program
|
|
181
202
|
.command('render')
|
|
182
203
|
.description('Render a template against a live SignalK server and write a PNG, without needing a device')
|
|
183
204
|
.requiredOption('-t, --template <path>', 'path to SVG template')
|
|
184
205
|
.requiredOption('-o, --output <path>', 'output PNG path')
|
|
185
|
-
.option('-u, --url <url>',
|
|
206
|
+
.option('-u, --url <url>', "SignalK server base URL - resolves the template's source=signalk/resources bindings - if omitted, tries each of " +
|
|
207
|
+
DEFAULT_SIGNALK_URLS.join(', ') +
|
|
208
|
+
' in turn')
|
|
186
209
|
.option('-e, --example-data <dir>', 'load vessels/resources from local example JSON files in <dir> (e.g. ./examples) instead of a live SignalK server - alternative to -u')
|
|
187
210
|
.option('-w, --width <px>', 'render width', '416')
|
|
188
211
|
.option('--height <px>', 'render height', '240')
|
|
@@ -199,7 +222,9 @@ program
|
|
|
199
222
|
.command('fields')
|
|
200
223
|
.description('List every <desc> binding in a template by element id, with its source spec and resolved value')
|
|
201
224
|
.requiredOption('-t, --template <path>', 'path to SVG template')
|
|
202
|
-
.option('-u, --url <url>',
|
|
225
|
+
.option('-u, --url <url>', "SignalK server base URL - resolves the template's source=signalk/resources bindings - if omitted, tries each of " +
|
|
226
|
+
DEFAULT_SIGNALK_URLS.join(', ') +
|
|
227
|
+
' in turn')
|
|
203
228
|
.option('-e, --example-data <dir>', 'load vessels/resources from local example JSON files in <dir> (e.g. ./examples) instead of a live SignalK server - alternative to -u')
|
|
204
229
|
.action(async (opts) => {
|
|
205
230
|
const doc = new xmldom_1.DOMParser().parseFromString(await (0, promises_1.readFile)(opts.template, 'utf-8'), 'image/svg+xml');
|
|
@@ -239,7 +264,9 @@ program
|
|
|
239
264
|
.command('field')
|
|
240
265
|
.description('Resolve a single binding spec directly against a live SignalK server, with no template')
|
|
241
266
|
.argument('<spec>', 'binding spec, e.g. "source=resources,resource=tides,path=station.name" or a bare SignalK path')
|
|
242
|
-
.option('-u, --url <url>',
|
|
267
|
+
.option('-u, --url <url>', "SignalK server base URL - resolves the spec's source=signalk/resources binding - if omitted, tries each of " +
|
|
268
|
+
DEFAULT_SIGNALK_URLS.join(', ') +
|
|
269
|
+
' in turn')
|
|
243
270
|
.option('-e, --example-data <dir>', 'load vessels/resources from local example JSON files in <dir> (e.g. ./examples) instead of a live SignalK server - alternative to -u')
|
|
244
271
|
.action(async (spec, opts) => {
|
|
245
272
|
const binding = (0, binding_1.parseBinding)(spec);
|
package/dist/config.js
CHANGED
|
@@ -168,7 +168,13 @@ function configSchema(app, discovered = []) {
|
|
|
168
168
|
repaintTrigger: { type: 'string', title: 'Repaint trigger', enum: ['subscription', 'interval'] },
|
|
169
169
|
triggerPath: { type: 'string', title: 'Trigger SignalK path (if repaint trigger is subscription)' },
|
|
170
170
|
intervalHours: { type: 'number', title: 'Repaint every N hours (if repaint trigger is interval)', minimum: 1 },
|
|
171
|
-
intervalMinute: {
|
|
171
|
+
intervalMinute: {
|
|
172
|
+
type: 'number',
|
|
173
|
+
title: 'Minutes past the hour (if repaint trigger is interval)',
|
|
174
|
+
minimum: 0,
|
|
175
|
+
maximum: 59,
|
|
176
|
+
default: 0,
|
|
177
|
+
},
|
|
172
178
|
aesKey: { type: 'string', title: 'BLE AES key (vendor-specific; leave blank to use a default key)' },
|
|
173
179
|
forceRepaint: {
|
|
174
180
|
type: 'boolean',
|
|
@@ -14,7 +14,7 @@ const DEVICE_DISCOVERY_TIMEOUT_MS = 30000;
|
|
|
14
14
|
/** Used while identifying a device during a scan - kept short since a scan may be enumerating several devices. */
|
|
15
15
|
const SCAN_CONNECT_TIMEOUT_MS = 10000;
|
|
16
16
|
/** Fallback when `VendorDeviceConfig.connectTimeoutMs` is omitted (e.g. a bare CLI `paint` call) - matches `defaultConfig().paintConnectTimeoutSeconds`. */
|
|
17
|
-
const DEFAULT_PAINT_CONNECT_TIMEOUT_MS =
|
|
17
|
+
const DEFAULT_PAINT_CONNECT_TIMEOUT_MS = 60000;
|
|
18
18
|
class ZhsunycoDriver {
|
|
19
19
|
constructor() {
|
|
20
20
|
this.vendor = 'zhsunyco';
|
|
@@ -65,9 +65,7 @@ class ZhsunycoDriver {
|
|
|
65
65
|
if (!info) {
|
|
66
66
|
throw new Error('zhsunyco device did not return valid config data');
|
|
67
67
|
}
|
|
68
|
-
const metadata = config.modelOverride
|
|
69
|
-
? { pid: info.pid, ...config.modelOverride }
|
|
70
|
-
: this.metadataForPid(info.pid, info.hwVersion);
|
|
68
|
+
const metadata = config.modelOverride ? { pid: info.pid, ...config.modelOverride } : this.metadataForPid(info.pid, info.hwVersion);
|
|
71
69
|
if (!metadata) {
|
|
72
70
|
throw new Error(`zhsunyco device reports unrecognised PID 0x${info.pid.toString(16).padStart(4, '0')} - ` +
|
|
73
71
|
'pass --width/--height/--voffset/--colours to describe it manually');
|
|
@@ -57,10 +57,7 @@ const AES_CHALLENGE_LENGTH = 16;
|
|
|
57
57
|
* reference driver's `BLE_SECRET_KEY`). Used as a fallback when a device hasn't been
|
|
58
58
|
* given its own key via the plugin config.
|
|
59
59
|
*/
|
|
60
|
-
exports.DEFAULT_BLE_AUTH = [
|
|
61
|
-
155, 96, 159, 40, 188, 73, 226, 87,
|
|
62
|
-
41, 189, 123, 141, 242, 43, 68, 32,
|
|
63
|
-
];
|
|
60
|
+
exports.DEFAULT_BLE_AUTH = [155, 96, 159, 40, 188, 73, 226, 87, 41, 189, 123, 141, 242, 43, 68, 32];
|
|
64
61
|
/** Resolves the configured per-device hex key, falling back to `DEFAULT_BLE_AUTH`. */
|
|
65
62
|
function resolveAesKey(aesKeyHex) {
|
|
66
63
|
return aesKeyHex ? Buffer.from(aesKeyHex, 'hex') : Buffer.from(exports.DEFAULT_BLE_AUTH);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rhizomatics/signalk-einklabel-plugin",
|
|
3
|
-
"version": "0.6.
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "0.6.2",
|
|
4
|
+
"description": "Display SignalK data on eInk Electronic Shelf Labels",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
7
7
|
"bin": {
|
|
@@ -19,11 +19,14 @@
|
|
|
19
19
|
"build": "tsc",
|
|
20
20
|
"watch": "tsc --watch",
|
|
21
21
|
"cli": "ts-node src/cli/index.ts",
|
|
22
|
-
"test": "node
|
|
22
|
+
"test": "node scripts/run-tests.js",
|
|
23
|
+
"coverage": "node scripts/run-tests.js --coverage",
|
|
24
|
+
"prettier:check": "prettier --check .",
|
|
25
|
+
"prettier:write": "prettier --write .",
|
|
23
26
|
"prepublishOnly": "npm run build"
|
|
24
27
|
},
|
|
25
28
|
"signalk": {
|
|
26
|
-
"displayName": "eInk Label
|
|
29
|
+
"displayName": "eInk Label Displays",
|
|
27
30
|
"appIcon": "docs/assets/icons/icon_tmp.png",
|
|
28
31
|
"screenshots": [
|
|
29
32
|
"docs/assets/screenshots/example_tidal_clock.png"
|
|
@@ -38,7 +41,8 @@
|
|
|
38
41
|
"esl",
|
|
39
42
|
"display",
|
|
40
43
|
"instrument",
|
|
41
|
-
"ble"
|
|
44
|
+
"ble",
|
|
45
|
+
"tides"
|
|
42
46
|
],
|
|
43
47
|
"engines": {
|
|
44
48
|
"node": ">=18"
|
|
@@ -61,6 +65,7 @@
|
|
|
61
65
|
"@types/luxon": "^3.7.1",
|
|
62
66
|
"@types/node": "^20.14.0",
|
|
63
67
|
"@types/pngjs": "^6.0.5",
|
|
68
|
+
"prettier": "^3.8.5",
|
|
64
69
|
"ts-node": "^10.9.2",
|
|
65
70
|
"typescript": "^5.5.0"
|
|
66
71
|
},
|
package/templates/tide.svg
CHANGED
|
@@ -269,4 +269,4 @@
|
|
|
269
269
|
id="moon_phase"
|
|
270
270
|
x="340.3974"
|
|
271
271
|
y="90.153488"><desc
|
|
272
|
-
id="desc22">path=environment.
|
|
272
|
+
id="desc22">path=environment.moon.phaseName,assets=../resources/svg/lunar_phases</desc></image></svg>
|