@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.
- package/CHANGELOG.md +15 -0
- package/README.md +36 -74
- package/package.json +2 -1
- package/plugin/autopilot/pypilot.js +312 -0
- package/plugin/index.js +23 -51
- package/plugin/lib/noaa.js +68 -98
- package/plugin/lib/openweather.js +90 -163
- package/plugin/openApi.json +350 -161
- package/plugin/weather/openweather.js +166 -0
- package/plugin/weather/weather-service.js +615 -0
- package/plugin/weather.js +435 -14
- package/public/3rdpartylicenses.txt +26 -0
- package/public/{760.75fe5894101f689d.js → 760.88e828fd37733cbd.js} +1 -1
- package/public/assets/help/favicon.ico +0 -0
- package/public/assets/help/img/ackalarms.png +0 -0
- package/public/assets/help/img/ais_list.png +0 -0
- package/public/assets/help/img/ais_popover.png +0 -0
- package/public/assets/help/img/ais_properties.png +0 -0
- package/public/assets/help/img/alarms.png +0 -0
- package/public/assets/help/img/anchor_alarm.png +0 -0
- package/public/assets/help/img/anchor_alarm_ack.png +0 -0
- package/public/assets/help/img/anchor_alarm_muted.png +0 -0
- package/public/assets/help/img/anchor_circle.png +0 -0
- package/public/assets/help/img/anchor_watch.png +0 -0
- package/public/assets/help/img/anchor_watch_adjust.png +0 -0
- package/public/assets/help/img/anchor_watch_rode.png +0 -0
- package/public/assets/help/img/buddy.png +0 -0
- package/public/assets/help/img/chart_list.png +0 -0
- package/public/assets/help/img/closest_approach.png +0 -0
- package/public/assets/help/img/context_menu.png +0 -0
- package/public/assets/help/img/course_data.png +0 -0
- package/public/assets/help/img/course_settings.png +0 -0
- package/public/assets/help/img/draw_menu.png +0 -0
- package/public/assets/help/img/gpx_load.png +0 -0
- package/public/assets/help/img/gpx_save.png +0 -0
- package/public/assets/help/img/navdata.png +0 -0
- package/public/assets/help/img/note_list.png +0 -0
- package/public/assets/help/img/playback.png +0 -0
- package/public/assets/help/img/route_list.png +0 -0
- package/public/assets/help/img/route_points_active.png +0 -0
- package/public/assets/help/img/route_popover.png +0 -0
- package/public/assets/help/img/screen.png +0 -0
- package/public/assets/help/img/settings_course.png +0 -0
- package/public/assets/help/img/settings_display.png +0 -0
- package/public/assets/help/img/settings_other_vessels.png +0 -0
- package/public/assets/help/img/settings_paths.png +0 -0
- package/public/assets/help/img/settings_resources_notes.png +0 -0
- package/public/assets/help/img/settings_signalk.png +0 -0
- package/public/assets/help/img/settings_units.png +0 -0
- package/public/assets/help/img/settings_vessels.png +0 -0
- package/public/assets/help/img/trail2route.png +0 -0
- package/public/assets/help/img/vessel_lines.png +0 -0
- package/public/assets/help/img/vessel_popover.png +0 -0
- package/public/assets/help/img/waypoint_list.png +0 -0
- package/public/assets/help/img/wpt_popover.png +0 -0
- package/public/assets/help/index.html +593 -499
- package/public/assets/icons/help-icon-150x150.png +0 -0
- package/public/assets/img/ais_active.png +0 -0
- package/public/assets/img/ais_buddy.png +0 -0
- package/public/assets/img/ais_inactive.png +0 -0
- package/public/assets/img/ais_self.png +0 -0
- package/public/assets/img/alarms/abandon.png +0 -0
- package/public/assets/img/alarms/adrift.png +0 -0
- package/public/assets/img/alarms/anchor.png +0 -0
- package/public/assets/img/alarms/arrivalCircleEntered.png +0 -0
- package/public/assets/img/alarms/collision.png +0 -0
- package/public/assets/img/alarms/cpa.png +0 -0
- package/public/assets/img/alarms/depth.png +0 -0
- package/public/assets/img/alarms/fire.png +0 -0
- package/public/assets/img/alarms/flooding.png +0 -0
- package/public/assets/img/alarms/grounding.png +0 -0
- package/public/assets/img/alarms/listing.png +0 -0
- package/public/assets/img/alarms/meteo.png +0 -0
- package/public/assets/img/alarms/mob.png +0 -0
- package/public/assets/img/alarms/piracy.png +0 -0
- package/public/assets/img/alarms/sinking.png +0 -0
- package/public/assets/img/anchor-limit.png +0 -0
- package/public/assets/img/anchor-radius-raised.png +0 -0
- package/public/assets/img/anchor-radius.png +0 -0
- package/public/assets/img/app_logo.png +0 -0
- package/public/assets/img/aton.png +0 -0
- package/public/assets/img/aton_basestation.png +0 -0
- package/public/assets/img/finishflag.png +0 -0
- package/public/assets/img/fixed_location.png +0 -0
- package/public/assets/img/gpx.png +0 -0
- package/public/assets/img/gpx_black.png +0 -0
- package/public/assets/img/marker-blue.png +0 -0
- package/public/assets/img/marker-green.png +0 -0
- package/public/assets/img/marker-red.png +0 -0
- package/public/assets/img/marker-yellow.png +0 -0
- package/public/assets/img/note.png +0 -0
- package/public/assets/img/sar_active.png +0 -0
- package/public/assets/img/ship_blur.png +0 -0
- package/public/assets/img/startboat.png +0 -0
- package/public/assets/img/startflag.png +0 -0
- package/public/assets/img/startup.png +0 -0
- package/public/assets/img/startup_ip5.png +0 -0
- package/public/assets/img/startup_ls.png +0 -0
- package/public/assets/img/startup_ls_1496x2048.png +0 -0
- package/public/assets/img/startup_pt_1536x2008.png +0 -0
- package/public/assets/img/startup_small.png +0 -0
- package/public/assets/img/weather_station.png +0 -0
- package/public/index.html +1 -1
- package/public/main.5c3e54222cc1e3ad.js +1 -0
- package/public/{runtime.f4c0016638982e6f.js → runtime.c7f9b6cf7269498e.js} +1 -1
- package/public/assets/img/alarms/weather.png +0 -0
- package/public/assets/img/marker-gold.png +0 -0
- package/public/main.15d6a4dcdfcbf90b.js +0 -1
- 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
|
|
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
|

|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-

|
|
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
|
|
107
|
+
Freeboard-SK allows you to use your favourite instrumentation apps installed on the Signal K server.
|
|
134
108
|
|
|
135
|
-
Select one or more
|
|
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
|
|
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
|
|
113
|
+
_Note: The `Signal K Instrument Panel` app will be displayed if no user selection has been made._
|
|
140
114
|
|
|
141
115
|

|
|
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
|
-
|
|
121
|
+
Features that are not ready for "prime time" are made available as experiments.
|
|
148
122
|
|
|
149
|
-
|
|
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
|
|
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:
|
|
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
|
-
|
|
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
|
|
243
|
-
- `/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
|
|
248
|
-
1. Execute `npm run build:
|
|
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.
|
|
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
|
|
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: '
|
|
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
|
-
|
|
40
|
-
type: '
|
|
41
|
-
title: '
|
|
42
|
-
default:
|
|
43
|
-
enum:
|
|
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
|
-
|
|
84
|
-
'ui:
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
157
|
-
|
|
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,
|
|
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) => {
|