@signalk/freeboard-sk 2.4.0 → 2.5.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +36 -74
  3. package/package.json +2 -1
  4. package/plugin/autopilot/pypilot.js +312 -0
  5. package/plugin/index.js +23 -51
  6. package/plugin/lib/noaa.js +68 -98
  7. package/plugin/lib/openweather.js +90 -163
  8. package/plugin/openApi.json +350 -161
  9. package/plugin/weather/openweather.js +166 -0
  10. package/plugin/weather/weather-service.js +615 -0
  11. package/plugin/weather.js +435 -14
  12. package/public/3rdpartylicenses.txt +26 -0
  13. package/public/{760.75fe5894101f689d.js → 760.88e828fd37733cbd.js} +1 -1
  14. package/public/assets/help/favicon.ico +0 -0
  15. package/public/assets/help/img/ackalarms.png +0 -0
  16. package/public/assets/help/img/ais_list.png +0 -0
  17. package/public/assets/help/img/ais_popover.png +0 -0
  18. package/public/assets/help/img/ais_properties.png +0 -0
  19. package/public/assets/help/img/alarms.png +0 -0
  20. package/public/assets/help/img/anchor_alarm.png +0 -0
  21. package/public/assets/help/img/anchor_alarm_ack.png +0 -0
  22. package/public/assets/help/img/anchor_alarm_muted.png +0 -0
  23. package/public/assets/help/img/anchor_circle.png +0 -0
  24. package/public/assets/help/img/anchor_watch.png +0 -0
  25. package/public/assets/help/img/anchor_watch_adjust.png +0 -0
  26. package/public/assets/help/img/anchor_watch_rode.png +0 -0
  27. package/public/assets/help/img/buddy.png +0 -0
  28. package/public/assets/help/img/chart_list.png +0 -0
  29. package/public/assets/help/img/closest_approach.png +0 -0
  30. package/public/assets/help/img/context_menu.png +0 -0
  31. package/public/assets/help/img/course_data.png +0 -0
  32. package/public/assets/help/img/course_settings.png +0 -0
  33. package/public/assets/help/img/draw_menu.png +0 -0
  34. package/public/assets/help/img/gpx_load.png +0 -0
  35. package/public/assets/help/img/gpx_save.png +0 -0
  36. package/public/assets/help/img/navdata.png +0 -0
  37. package/public/assets/help/img/note_list.png +0 -0
  38. package/public/assets/help/img/playback.png +0 -0
  39. package/public/assets/help/img/route_list.png +0 -0
  40. package/public/assets/help/img/route_points_active.png +0 -0
  41. package/public/assets/help/img/route_popover.png +0 -0
  42. package/public/assets/help/img/screen.png +0 -0
  43. package/public/assets/help/img/settings_course.png +0 -0
  44. package/public/assets/help/img/settings_display.png +0 -0
  45. package/public/assets/help/img/settings_other_vessels.png +0 -0
  46. package/public/assets/help/img/settings_paths.png +0 -0
  47. package/public/assets/help/img/settings_resources_notes.png +0 -0
  48. package/public/assets/help/img/settings_signalk.png +0 -0
  49. package/public/assets/help/img/settings_units.png +0 -0
  50. package/public/assets/help/img/settings_vessels.png +0 -0
  51. package/public/assets/help/img/trail2route.png +0 -0
  52. package/public/assets/help/img/vessel_lines.png +0 -0
  53. package/public/assets/help/img/vessel_popover.png +0 -0
  54. package/public/assets/help/img/waypoint_list.png +0 -0
  55. package/public/assets/help/img/wpt_popover.png +0 -0
  56. package/public/assets/help/index.html +593 -499
  57. package/public/assets/icons/help-icon-150x150.png +0 -0
  58. package/public/assets/img/ais_active.png +0 -0
  59. package/public/assets/img/ais_buddy.png +0 -0
  60. package/public/assets/img/ais_inactive.png +0 -0
  61. package/public/assets/img/ais_self.png +0 -0
  62. package/public/assets/img/alarms/abandon.png +0 -0
  63. package/public/assets/img/alarms/adrift.png +0 -0
  64. package/public/assets/img/alarms/anchor.png +0 -0
  65. package/public/assets/img/alarms/arrivalCircleEntered.png +0 -0
  66. package/public/assets/img/alarms/collision.png +0 -0
  67. package/public/assets/img/alarms/cpa.png +0 -0
  68. package/public/assets/img/alarms/depth.png +0 -0
  69. package/public/assets/img/alarms/fire.png +0 -0
  70. package/public/assets/img/alarms/flooding.png +0 -0
  71. package/public/assets/img/alarms/grounding.png +0 -0
  72. package/public/assets/img/alarms/listing.png +0 -0
  73. package/public/assets/img/alarms/meteo.png +0 -0
  74. package/public/assets/img/alarms/mob.png +0 -0
  75. package/public/assets/img/alarms/piracy.png +0 -0
  76. package/public/assets/img/alarms/sinking.png +0 -0
  77. package/public/assets/img/anchor-limit.png +0 -0
  78. package/public/assets/img/anchor-radius-raised.png +0 -0
  79. package/public/assets/img/anchor-radius.png +0 -0
  80. package/public/assets/img/app_logo.png +0 -0
  81. package/public/assets/img/aton.png +0 -0
  82. package/public/assets/img/aton_basestation.png +0 -0
  83. package/public/assets/img/finishflag.png +0 -0
  84. package/public/assets/img/fixed_location.png +0 -0
  85. package/public/assets/img/gpx.png +0 -0
  86. package/public/assets/img/gpx_black.png +0 -0
  87. package/public/assets/img/marker-blue.png +0 -0
  88. package/public/assets/img/marker-green.png +0 -0
  89. package/public/assets/img/marker-red.png +0 -0
  90. package/public/assets/img/marker-yellow.png +0 -0
  91. package/public/assets/img/note.png +0 -0
  92. package/public/assets/img/sar_active.png +0 -0
  93. package/public/assets/img/ship_blur.png +0 -0
  94. package/public/assets/img/startboat.png +0 -0
  95. package/public/assets/img/startflag.png +0 -0
  96. package/public/assets/img/startup.png +0 -0
  97. package/public/assets/img/startup_ip5.png +0 -0
  98. package/public/assets/img/startup_ls.png +0 -0
  99. package/public/assets/img/startup_ls_1496x2048.png +0 -0
  100. package/public/assets/img/startup_pt_1536x2008.png +0 -0
  101. package/public/assets/img/startup_small.png +0 -0
  102. package/public/assets/img/weather_station.png +0 -0
  103. package/public/index.html +1 -1
  104. package/public/main.5c3e54222cc1e3ad.js +1 -0
  105. package/public/{runtime.f4c0016638982e6f.js → runtime.c7f9b6cf7269498e.js} +1 -1
  106. package/public/assets/img/alarms/weather.png +0 -0
  107. package/public/assets/img/marker-gold.png +0 -0
  108. package/public/main.15d6a4dcdfcbf90b.js +0 -1
  109. package/signalk-freeboard-sk-2.4.0.tgz +0 -0
package/CHANGELOG.md CHANGED
@@ -1,5 +1,20 @@
1
1
  # CHANGELOG: Freeboard
2
2
 
3
+ ### v2.5.0
4
+
5
+ - **Added**: In the chart list, toggle the display of the bounds of all available charts on the map.
6
+ - **Added**: Displaying vector charts with defined styles in a Mapbox style JSON file.
7
+ - **Updated**: New Map icons for waypoints and AIS targets.
8
+
9
+ **Weather** moves from `experiment` -> feature with the introduction of the `meteo` context as per [nmea0183-signalk PR #245 - VDM Msg 8](https://github.com/SignalK/nmea0183-signalk).
10
+
11
+ - **Added**: Display weather stations on the map and view observation data.
12
+ - **Added**: Weather data relative to vessel position, sourced from _OpenWeather_, including hourly forecasts is available via the `meteo.freeboard-sk` context.
13
+ - **Removed**: Option to use NOAA as weather source.
14
+
15
+ **Experiments**:
16
+ - **Routes**: Render start / Finish line for the active route when values are present in route meta data.
17
+
3
18
  ### v2.4.0
4
19
 
5
20
  - **Added**: Ability to shift anchor position.
package/README.md CHANGED
@@ -1,11 +1,18 @@
1
1
  # Freeboard-SK
2
- Freeboard-SK is a stateless, multi-station, Openlayers based chart plotter for Signal K for displaying and managing routes, waypoints, notes, alarms, notifications and more from any web enabled device.
2
+ Freeboard-SK is a stateless, multi-station, Openlayers based chart plotter for Signal K.
3
+ Use it to display:
4
+ - Resources _(i.e. routes, waypoints, notes, charts, etc)_
5
+ - Alarms & notifications
6
+ - AIS information
7
+ - Weather information
8
+ - Signal K instrument WebApps.
9
+
10
+ and more from any web enabled device.
3
11
 
4
12
  ![screen](https://user-images.githubusercontent.com/38519157/128667564-0f5e1ed6-eaae-40c7-ad62-5e7011c1f082.png)
5
13
 
6
14
 
7
15
  ## Features:
8
- ---
9
16
 
10
17
  ### Vessel / Chart Display:
11
18
 
@@ -72,52 +79,19 @@ Whilst not specifically defined in the Signal K specification, Freeboard-SK supp
72
79
 
73
80
  Freeboard-SK can display alarms _(visual and audio)_ & messages contained in *Notification* messages transmitted by the Signal K server.
74
81
 
75
- Alarm types supported include:
82
+ Additionally you can set alarms, including _anchor watch_, as well as raise alarms such as _man overboard_, _sinking_, etc directly from the user interface.
83
+
84
+ Supported alarm types include:
76
85
  - Depth
77
86
  - Closest Approach
78
- - Anchor drag
87
+ - Anchor drag / watch
79
88
  - "Buddy" notifications
80
- - All standard Signal K Alarms (i.e. `Man overboard`, `Sinking`, etc.)
81
-
82
- Additionally you can raise `anchor watch` and a number of the standard alarms such as `Man overboard`, `Sinking`, etc directly from the user interface.
83
-
84
- Freeboard-SK also implements the following API endpoint to accept requests for raising and clearing notifications for Signal K Standard Alarms:
85
- ```
86
- /signalk/v2/api/notifications/<standard_alarm>
87
- ```
88
-
89
- _Example: Raise Man overboard alarm (default message):_
90
- ```
91
- HTTP PUT 'http://host:port/signalk.v2/api/notifications/mob'
92
- ```
93
-
94
- _Example: Raise Man overboard alarm (custom message):_
95
- ```
96
- HTTP PUT 'http://host:port/signalk.v2/api/notifications/mob' {
97
- "message": "Man Overboard!"
98
- }
99
- ```
89
+ - All Signal K specification defined alarms.
100
90
 
101
- _Example: Clear Man overboard alarm:_
102
- ```
103
- HTTP DELETE 'http://host:port/signalk.v2/api/notifications/mob'
104
- ```
105
91
 
106
- _**Signal K Standard Alarms:**_
107
- - mob
108
- - fire
109
- - sinking
110
- - flooding
111
- - collision
112
- - grounding
113
- - listing
114
- - adrift
115
- - piracy
116
- - abandon
117
-
118
- _Vessel Closest Approach alarm:_
119
- ![screen](https://user-images.githubusercontent.com/38519157/128667564-0f5e1ed6-eaae-40c7-ad62-5e7011c1f082.png)
92
+ Freeboard-SK also implements API endpoints to accept requests for raising and clearing Signal K specification defined alarms.
120
93
 
94
+ _See OpenAPI documentation in Signal K Server Admin UI for details._
121
95
 
122
96
 
123
97
  ---
@@ -130,13 +104,13 @@ Freeboard-SK supports the Siganl K `playback` api and can replay recorded time-s
130
104
 
131
105
  ### Instruments:
132
106
 
133
- Freeboard-SK allows you to use your favourite instrumentation app installed on the Signal K server.
107
+ Freeboard-SK allows you to use your favourite instrumentation apps installed on the Signal K server.
134
108
 
135
- Select one or more from the installed applications listed in the `settings` screen and they will displayed in the instrument panel drawer.
109
+ Select one or more installed applications listed in the `settings` screen and they will displayed in the instrument drawer.
136
110
 
137
- When more than one app is selected you can cycle through them within the instrument panel.
111
+ When more than one app is selected you can cycle through them within the instrument drawer.
138
112
 
139
- _Note: The `Instrument Panel` app is the default application if no user selection has been made._
113
+ _Note: The `Signal K Instrument Panel` app will be displayed if no user selection has been made._
140
114
 
141
115
  ![instruments](https://user-images.githubusercontent.com/38519157/128668406-02cbb8d8-2353-4e93-ae5e-12e0c7d507fe.png)
142
116
 
@@ -144,17 +118,18 @@ _Note: The `Instrument Panel` app is the default application if no user selectio
144
118
 
145
119
  ### Experiments:
146
120
 
147
- To get access to experimental Freeboard-SK features ensure you check the **Experimental Features** option in **Settings**.
121
+ Features that are not ready for "prime time" are made available as experiments.
148
122
 
149
- Checking this option will make these features appear in the user interface.
123
+ To make experimental features available from within the Freeboard-SK user interface, you need to ensure the **Experimental Features** option is checked in **Settings**.
124
+
125
+ _Note: Some experiments will require configuration of Freeboard-SK via the _Plugin Config_ screen of the Signal K Server Admin UI._
150
126
 
151
127
  ---
152
128
 
153
129
  ## System Requirements:
154
130
 
155
- Freeboard-SK requires **Signal K API Version 2** as it makes use of both the v2 `Resources` and `Course` APIs.
131
+ **Freeboard-SK requires _Signal K Server Version 2.0 or above**.
156
132
 
157
- _If installed on Signal K v1.x.x Freeboard features may not be fully functional!_
158
133
 
159
134
  The following features require that the Signal K server have plugins / providers installed to service the following paths:
160
135
 
@@ -176,6 +151,7 @@ The following features require that the Signal K server have plugins / providers
176
151
  The following plugins are recommended for the *Signal K node server* to enable full functionality:
177
152
 
178
153
  - @signalk/charts-plugin *(Charts provider)*
154
+ - signalk-pmtiles-plugin *(ProtoMaps chart provider)*
179
155
  - signalk-anchoralarm-plugin _(anchor alarm settings & notifications)_
180
156
  - signalk-simple-notifications _(depth alarm notifications)_
181
157
  - @signalk/course-provider _(course calculations e.g. XTE, DTG, etc.)_
@@ -209,43 +185,29 @@ DEV_SERVER {
209
185
 
210
186
  _Note: These settings apply in **Development Mode** only!_
211
187
 
212
- ```
213
- npm start
214
- ng serve
215
- ng build
216
- ```
217
-
218
-
219
188
  _They will __NOT__ apply when using **Production Mode**, the generated application will attempt to connect to a Signal K api / stream on the hosting server._
220
189
 
221
- ```
222
- ng build -c production
223
- or
224
- npm run build:web
225
- ```
226
-
227
190
  ---
228
191
 
229
192
  ### Building a Release:
230
193
 
231
194
  __Building the Application:__
232
195
 
233
- To build all components of the application ready for release use the `npm run build:all` command.
234
-
235
- __Building individual components:__
196
+ To build all components of the application _(plugin and webapp)_ ready for release use the `npm run build:prod` command.
236
197
 
237
- - To build only the _web application_ use the `npm run build:web` command.
238
- - To build only the _helper plugin_ use the `npm run build:helper` command.
198
+ __Building components individually:__
239
199
 
200
+ - To build only the _webapp_ use the command `npm run build:web`.
201
+ - To build only the _helper plugin_ use the command `npm run build:helper`.
240
202
 
241
- Built files for deployment are placed in the following folders:
242
- - `/public` (web application)
243
- - `/plugin` (helper plugin)
203
+ Built files _(for deployment)_ are placed in the following folders:
204
+ - `/public` _(Freeboard-SK web app)_
205
+ - `/plugin` _(Freeboard-SK plugin)_
244
206
 
245
- __Building NPM package:__
207
+ __Building the NPM package:__
246
208
 
247
- To build the NPM package use `npm pack` command which will:
248
- 1. Execute `npm run build:all`
209
+ To build the NPM package use the `npm pack` command to:
210
+ 1. Execute `npm run build:prod`
249
211
  1. Create the NPM package (`.tgz`) file in the root folder of the project.
250
212
 
251
213
  ---
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@signalk/freeboard-sk",
3
- "version": "2.4.0",
3
+ "version": "2.5.0",
4
4
  "description": "Openlayers chart plotter implementation for Signal K",
5
5
  "keywords": [
6
6
  "signalk-webapp",
@@ -80,6 +80,7 @@
80
80
  "ng-packagr": "^16.2.0",
81
81
  "ngeohash": "^0.6.3",
82
82
  "ol": "^8.2.0",
83
+ "ol-mapbox-style": "^12.1.1",
83
84
  "pmtiles": "^2.7.0",
84
85
  "prettier": "^2.5.1",
85
86
  "prettier-plugin-organize-attributes": "^0.0.5",
@@ -0,0 +1,312 @@
1
+ "use strict";
2
+ // **** Experiment: PyPilot integration ****
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.closePyPilot = exports.initPyPilot = void 0;
5
+ const server_api_1 = require("@signalk/server-api");
6
+ const socket_io_client_1 = require("socket.io-client");
7
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
8
+ const apData = {
9
+ options: {
10
+ states: ['enabled', 'disabled'],
11
+ modes: []
12
+ },
13
+ state: null,
14
+ mode: null,
15
+ target: null,
16
+ engaged: false
17
+ };
18
+ let server;
19
+ let pluginId;
20
+ let socket;
21
+ const AUTOPILOT_API_PATH = '/signalk/v2/api/vessels/self/steering/autopilot';
22
+ const DEAFULT_AP = `${AUTOPILOT_API_PATH}/default`;
23
+ // initialise connection to autopilot, register socket listeners and SK AP path PUT handlers
24
+ const initPyPilot = (app, id, config) => {
25
+ server = app;
26
+ pluginId = id;
27
+ server.debug(`** Connecting to PyPilot **`);
28
+ socket = (0, socket_io_client_1.io)(`http://${config.host}:${config.port}`);
29
+ if (!socket) {
30
+ console.log(`PyPilot NOT connected @ ${config.host}:${config.port}... ensure 'pypilot_web' is running.`);
31
+ }
32
+ // API endpoints
33
+ initApiRoutes();
34
+ emitAPDelta('defaultPilot', pluginId);
35
+ initPyPilotListeners();
36
+ };
37
+ exports.initPyPilot = initPyPilot;
38
+ const closePyPilot = () => {
39
+ if (socket) {
40
+ socket.close();
41
+ }
42
+ };
43
+ exports.closePyPilot = closePyPilot;
44
+ const initApiRoutes = () => {
45
+ server.debug(`** Registering API endpoint(s): ${AUTOPILOT_API_PATH} **`);
46
+ server.get(`${DEAFULT_AP}`, (req, res) => {
47
+ server.debug(`${req.method} ${DEAFULT_AP}`);
48
+ res.status(200);
49
+ res.json(apData);
50
+ });
51
+ server.get(`${AUTOPILOT_API_PATH}`, (req, res) => {
52
+ server.debug(`${req.method} ${AUTOPILOT_API_PATH}`);
53
+ res.status(200).json({
54
+ 'freeboard-sk': { provider: 'freeboard-sk', isDefault: true }
55
+ });
56
+ });
57
+ server.get(`${AUTOPILOT_API_PATH}/defaultPilot`, (req, res) => {
58
+ server.debug(`${req.method} ${AUTOPILOT_API_PATH}/defaultPilot`);
59
+ res.status(200).json({ id: pluginId });
60
+ });
61
+ server.get(`${DEAFULT_AP}/state`, (req, res) => {
62
+ server.debug(`${req.method} ${DEAFULT_AP}/state`);
63
+ res.status(200).json({ value: apData.state });
64
+ });
65
+ server.put(`${DEAFULT_AP}/state`, (req, res) => {
66
+ server.debug(`${req.method} ${DEAFULT_AP}/state`);
67
+ if (typeof req.body.value === 'undefined') {
68
+ res.status(400).json({
69
+ state: 'FAILED',
70
+ statusCode: 400,
71
+ message: `Error: Invalid value supplied!`
72
+ });
73
+ return;
74
+ }
75
+ if (!apData.options.states.includes(req.body.value)) {
76
+ res.status(400).json({
77
+ state: 'FAILED',
78
+ statusCode: 400,
79
+ message: `Error: Invalid value supplied!`
80
+ });
81
+ return;
82
+ }
83
+ const r = sendToPyPilot('state', req.body.value);
84
+ res.status(r.statusCode).json(r);
85
+ });
86
+ server.get(`${DEAFULT_AP}/mode`, (req, res) => {
87
+ server.debug(`${req.method} ${DEAFULT_AP}/mode`);
88
+ res.status(200).json({ value: apData.mode });
89
+ });
90
+ server.put(`${DEAFULT_AP}/mode`, (req, res) => {
91
+ server.debug(`${req.method} ${DEAFULT_AP}/mode`);
92
+ if (typeof req.body.value === 'undefined') {
93
+ res.status(400).json({
94
+ state: 'FAILED',
95
+ statusCode: 400,
96
+ message: `Error: Invalid value supplied!`
97
+ });
98
+ return;
99
+ }
100
+ if (!apData.options.modes.includes(req.body.value)) {
101
+ res.status(400).json({
102
+ state: 'FAILED',
103
+ statusCode: 400,
104
+ message: `Error: Invalid value supplied!`
105
+ });
106
+ return;
107
+ }
108
+ const r = sendToPyPilot('mode', req.body.value);
109
+ res.status(r.statusCode).json(r);
110
+ });
111
+ server.put(`${DEAFULT_AP}/target`, (req, res) => {
112
+ server.debug(`${req.method} ${DEAFULT_AP}/target`);
113
+ if (typeof req.body.value !== 'number') {
114
+ res.status(400).json({
115
+ state: 'FAILED',
116
+ statusCode: 400,
117
+ message: `Error: Invalid value supplied!`
118
+ });
119
+ return;
120
+ }
121
+ let deg = req.body.value * (180 / Math.PI);
122
+ if (deg > 359) {
123
+ deg = 359;
124
+ }
125
+ else if (deg < -179) {
126
+ deg = -179;
127
+ }
128
+ const r = sendToPyPilot('target', deg);
129
+ res.status(r.statusCode).json(r);
130
+ });
131
+ server.put(`${DEAFULT_AP}/target/adjust`, (req, res) => {
132
+ server.debug(`${req.method} ${DEAFULT_AP}/target/adjust`);
133
+ if (typeof req.body.value !== 'number') {
134
+ res.status(400).json({
135
+ state: 'FAILED',
136
+ statusCode: 400,
137
+ message: `Error: Invalid value supplied!`
138
+ });
139
+ return;
140
+ }
141
+ const v = req.body.value * (180 / Math.PI);
142
+ let deg = apData.target + v;
143
+ if (deg > 360) {
144
+ deg = 360;
145
+ }
146
+ else if (deg < -180) {
147
+ deg = -180;
148
+ }
149
+ const r = sendToPyPilot('target', deg);
150
+ res.status(r.statusCode).json(r);
151
+ });
152
+ server.post(`${DEAFULT_AP}/engage`, (req, res) => {
153
+ server.debug(`${req.method} ${DEAFULT_AP}/engage`);
154
+ const r = sendToPyPilot('state', 'enabled');
155
+ res.status(r.statusCode).json(r);
156
+ });
157
+ server.post(`${DEAFULT_AP}/disengage`, (req, res) => {
158
+ server.debug(`${req.method} ${DEAFULT_AP}/disengage`);
159
+ const r = sendToPyPilot('state', 'disabled');
160
+ res.status(r.statusCode).json(r);
161
+ });
162
+ server.post(`${DEAFULT_AP}/tack/port`, (req, res) => {
163
+ server.debug(`${req.method} ${DEAFULT_AP}/tack/port`);
164
+ const r = sendToPyPilot('tack', 'port');
165
+ res.status(r.statusCode).json(r);
166
+ });
167
+ server.post(`${DEAFULT_AP}/tack/starboard`, (req, res) => {
168
+ server.debug(`${req.method} ${DEAFULT_AP}/tack/starboard`);
169
+ const r = sendToPyPilot('tack', 'starboard');
170
+ res.status(r.statusCode).json(r);
171
+ });
172
+ server.post(`${DEAFULT_AP}/gybe/port`, (req, res) => {
173
+ server.debug(`${req.method} ${DEAFULT_AP}/gybe/port`);
174
+ res.status(501).json({
175
+ state: 'COMPLETED',
176
+ statusCode: 501,
177
+ message: 'Not implemented!'
178
+ });
179
+ });
180
+ server.post(`${DEAFULT_AP}/gybe/starboard`, (req, res) => {
181
+ server.debug(`${req.method} ${DEAFULT_AP}/gybe/starboard`);
182
+ res.status(501).json({
183
+ state: 'COMPLETED',
184
+ statusCode: 501,
185
+ message: 'Not implemented!'
186
+ });
187
+ });
188
+ };
189
+ // PyPilot socket event listeners
190
+ const initPyPilotListeners = () => {
191
+ socket.on('connect', () => {
192
+ server.debug('socket connected...');
193
+ const msg = `Started: Connected to PyPilot.`;
194
+ server.setPluginStatus(msg);
195
+ setTimeout(() => {
196
+ const period = 1;
197
+ socket.emit('pypilot', `watch={"ap.heading": ${JSON.stringify(period)}}`);
198
+ socket.emit('pypilot', `watch={"ap.heading_command": ${JSON.stringify(period)}}`);
199
+ socket.emit('pypilot', `watch={"ap.enabled": ${JSON.stringify(period)}}`);
200
+ socket.emit('pypilot', `watch={"ap.mode": ${JSON.stringify(period)}}`);
201
+ }, 1000);
202
+ });
203
+ socket.on('connect_error', () => {
204
+ server.debug('socket connect_error!');
205
+ server.setPluginStatus(`Unable to connect to PyPilot!`);
206
+ });
207
+ // pypilot updates listener
208
+ socket.on('pypilot', (msg) => {
209
+ handlePyPilotUpdateMsg(JSON.parse(msg));
210
+ });
211
+ // pypilot_values listener
212
+ socket.on('pypilot_values', (msg) => {
213
+ handlePyPilotValuesMsg(JSON.parse(msg));
214
+ });
215
+ };
216
+ // Send values to pypilot
217
+ const sendToPyPilot = (command, value) => {
218
+ server.debug(`sendToPyPilot: ${command} = ${value}`);
219
+ let mode = '';
220
+ if (command === 'mode') {
221
+ if (typeof value === 'string') {
222
+ mode = 'ap.mode';
223
+ }
224
+ }
225
+ else if (command === 'state') {
226
+ if (typeof value === 'string') {
227
+ value = value === 'enabled' ? true : false;
228
+ mode = 'ap.enabled';
229
+ }
230
+ }
231
+ else if (command === 'target') {
232
+ server.debug(`command: ${command}, value: ${value}, ${typeof value}`);
233
+ if (typeof value === 'number') {
234
+ mode = 'ap.heading_command';
235
+ }
236
+ }
237
+ else {
238
+ server.debug('Error: Invalid command!');
239
+ return {
240
+ state: 'FAILED',
241
+ statusCode: 404,
242
+ message: `Invalid command!`
243
+ };
244
+ }
245
+ try {
246
+ server.debug(`out -> ${mode}=${JSON.stringify(value)}`);
247
+ socket.emit('pypilot', mode + '=' + JSON.stringify(value));
248
+ return {
249
+ state: 'COMPLETED',
250
+ statusCode: 200
251
+ };
252
+ }
253
+ catch (error) {
254
+ return {
255
+ state: 'FAILED',
256
+ statusCode: 404,
257
+ message: `Invalid command!`
258
+ };
259
+ }
260
+ };
261
+ // process received pypilot update messages and send SK delta
262
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
263
+ const handlePyPilotUpdateMsg = (data) => {
264
+ //server.debug(`-> pilot data = ${JSON.stringify(data)}`);
265
+ if (typeof data['ap.heading_command'] !== 'undefined') {
266
+ server.debug(`ap.heading_command -> ${data['ap.heading_command']}`);
267
+ const h = data['ap.heading_command'] === false ? null : data['ap.heading_command'];
268
+ apData.target = h;
269
+ const h_rad = (Math.PI / 180) * apData.target;
270
+ server.debug(`ap.heading_command -> deg: ${apData.target}, rad: ${h_rad}`);
271
+ emitAPDelta('target', h_rad);
272
+ }
273
+ if (typeof data['ap.mode'] !== 'undefined') {
274
+ apData.mode = data['ap.mode'];
275
+ server.debug(`ap.mode -> ${data['ap.mode']}`);
276
+ emitAPDelta('mode', apData.mode);
277
+ }
278
+ if (typeof data['ap.enabled'] !== 'undefined') {
279
+ apData.state = data['ap.enabled'] ? 'enabled' : 'disabled';
280
+ apData.engaged = apData.state === 'enabled' ? true : false;
281
+ server.debug(`ap.enabled -> ${data['ap.enabled']}`);
282
+ emitAPDelta('state', apData.state);
283
+ emitAPDelta('engaged', apData.engaged);
284
+ }
285
+ };
286
+ // process received pypilot_values message and send SK delta
287
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
288
+ const handlePyPilotValuesMsg = (data) => {
289
+ // available modes
290
+ if (typeof data['ap.mode'] !== undefined && data['ap.mode'].choices) {
291
+ apData.options.modes = Array.isArray(data['ap.mode'].choices)
292
+ ? data['ap.mode'].choices
293
+ : [];
294
+ }
295
+ };
296
+ // emit SK delta steering.autopilot.xxx
297
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
298
+ const emitAPDelta = (path, value) => {
299
+ const pathRoot = 'steering.autopilot';
300
+ const msg = {
301
+ path: `${pathRoot}.${path}`,
302
+ value: value
303
+ };
304
+ server.debug(`delta ${path} -> ${JSON.stringify(msg)}`);
305
+ server.handleMessage(pluginId, {
306
+ updates: [
307
+ {
308
+ values: [msg]
309
+ }
310
+ ]
311
+ }, server_api_1.SKVersion.v2);
312
+ };
package/plugin/index.js CHANGED
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const alarms_1 = require("./alarms/alarms");
4
- const weather_1 = require("./weather");
5
- const pypilot_1 = require("./pypilot");
4
+ const weather_service_1 = require("./weather/weather-service");
5
+ const pypilot_1 = require("./autopilot/pypilot");
6
6
  const openapi = require("./openApi.json");
7
+ const defaultPollInterval = 60;
7
8
  const CONFIG_SCHEMA = {
8
9
  properties: {
9
10
  alarms: {
@@ -22,7 +23,7 @@ const CONFIG_SCHEMA = {
22
23
  weather: {
23
24
  type: 'object',
24
25
  title: 'Weather API.',
25
- description: 'Weather service settings.',
26
+ description: 'OpenWeather service settings.',
26
27
  properties: {
27
28
  enable: {
28
29
  type: 'boolean',
@@ -36,12 +37,12 @@ const CONFIG_SCHEMA = {
36
37
  default: '',
37
38
  description: 'Get your API key at https://openweathermap.org/home/sign_up'
38
39
  },
39
- service: {
40
- type: 'string',
41
- title: 'Weather service',
42
- default: 'openweather',
43
- enum: weather_1.WEATHER_SERVICES,
44
- description: 'Select the weather service'
40
+ pollInterval: {
41
+ type: 'number',
42
+ title: 'Polling Interval',
43
+ default: 60,
44
+ enum: weather_service_1.WEATHER_POLL_INTERVAL,
45
+ description: 'Select the interval at which the weather service is polled.'
45
46
  }
46
47
  }
47
48
  },
@@ -78,10 +79,13 @@ const CONFIG_UISCHEMA = {
78
79
  'ui:help': ' '
79
80
  },
80
81
  apiKey: {
81
- 'ui:disabled': false
82
+ 'ui:disabled': false,
83
+ 'ui-help': ''
82
84
  },
83
- service: {
84
- 'ui:disabled': false
85
+ pollInterval: {
86
+ 'ui:widget': 'select',
87
+ 'ui:title': 'Polling Interval (mins)',
88
+ 'ui:help': ' '
85
89
  }
86
90
  }
87
91
  };
@@ -94,7 +98,7 @@ module.exports = (server) => {
94
98
  weather: {
95
99
  enable: false,
96
100
  apiKey: '',
97
- service: 'openweather'
101
+ pollInterval: defaultPollInterval
98
102
  },
99
103
  pypilot: {
100
104
  enable: false,
@@ -130,11 +134,12 @@ module.exports = (server) => {
130
134
  settings.weather = options.weather ?? {
131
135
  enable: false,
132
136
  apiKey: '',
133
- service: 'openweather'
137
+ pollInterval: defaultPollInterval
134
138
  };
135
139
  settings.weather.enable = options.weather.enable ?? false;
136
140
  settings.weather.apiKey = options.weather.apiKey ?? '';
137
- settings.weather.service = options.weather.service ?? 'openweather';
141
+ settings.weather.pollInterval =
142
+ options.weather.pollInterval ?? defaultPollInterval;
138
143
  settings.alarms = options.alarms ?? {
139
144
  enable: true
140
145
  };
@@ -153,11 +158,8 @@ module.exports = (server) => {
153
158
  }
154
159
  let msg = '';
155
160
  if (settings.weather.enable) {
156
- const result = registerProvider('weather');
157
- msg = `Started - ${result.length !== 0
158
- ? `${result} not registered!`
159
- : 'Providing: weather'}`;
160
- (0, weather_1.initWeather)(server, plugin.id, settings.weather);
161
+ msg = `Started - Providing: weather`;
162
+ (0, weather_service_1.initWeather)(server, plugin.id, settings.weather);
161
163
  }
162
164
  if (settings.pypilot.enable) {
163
165
  (0, pypilot_1.initPyPilot)(server, plugin.id, settings.pypilot);
@@ -175,42 +177,12 @@ module.exports = (server) => {
175
177
  };
176
178
  const doShutdown = () => {
177
179
  server.debug('** shutting down **');
178
- (0, weather_1.stopWeather)();
180
+ (0, weather_service_1.stopWeather)();
179
181
  (0, pypilot_1.closePyPilot)();
180
182
  server.debug('** Un-subscribing from events **');
181
183
  const msg = 'Stopped';
182
184
  server.setPluginStatus(msg);
183
185
  };
184
- const registerProvider = (resType) => {
185
- let failed = '';
186
- try {
187
- server.registerResourceProvider({
188
- type: resType,
189
- methods: {
190
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
191
- listResources: (params) => {
192
- return (0, weather_1.listWeather)(params);
193
- },
194
- getResource: (path, property) => {
195
- return (0, weather_1.getWeather)(path, property);
196
- },
197
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
198
- setResource: (id, value) => {
199
- server.debug(`${id}, ${value}`);
200
- throw 'Not implemented!';
201
- },
202
- deleteResource: (id) => {
203
- server.debug(`${id}`);
204
- throw 'Not implemented!';
205
- }
206
- }
207
- });
208
- }
209
- catch (error) {
210
- failed = resType;
211
- }
212
- return failed;
213
- };
214
186
  const initApiEndpoints = (router) => {
215
187
  server.debug(`Initialising Freeboard-SK plugin endpoints.......`);
216
188
  router.get('/settings', (req, res) => {