diff --git a/.github/workflows/build-docker.yml b/.github/workflows/build-docker.yml index b35e21a47..10d8a5b9e 100644 --- a/.github/workflows/build-docker.yml +++ b/.github/workflows/build-docker.yml @@ -5,6 +5,7 @@ on: branches: - master - "build-docker" + - documentation tags: - '*' - '!v*' diff --git a/.gitignore b/.gitignore index f09b964cd..b90e7bff9 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,5 @@ test/server-test-config/applicationData/ test/server-test-config/ssl-cert.pem test/server-test-config/ssl-key.pem test/server-test-config/plugin-config-data/ + +docs/built diff --git a/.npmignore b/.npmignore index 6c232ae31..0e44bfa3c 100644 --- a/.npmignore +++ b/.npmignore @@ -72,4 +72,8 @@ publishing.md bin/linkpackages fly_io -.github \ No newline at end of file +.github + +docs/src +docs/book.toml +docs/server-guide/**/*.dia diff --git a/README.md b/README.md index 397d0170c..2e1549638 100644 --- a/README.md +++ b/README.md @@ -9,12 +9,10 @@ ## Contents * [Introduction](#introduction) * [Signal K Platform](#signal-k-platform) -* [Community & support](#community--support) +* [Documentation, Community & support](#documentation-community--support) * [How to get Signal K Server](#how-to-get-signal-k-server) * [Configuration and use](#configuration-and-use) * [Supported PGNs, Sentences and more](#supported-pgns-sentences-and-more) -* [Environment variables](#environment-variables) -* [Command line options](#command-line-options) * [Development](#development) * [Sponsoring Signal K](#sponsoring-signal-k) * [License](#license) @@ -38,11 +36,11 @@ For Marine vendors who build marine hardware and software, for example those dev Signal K Server is already running inside products developed by Victron Energy, Airmar Technology and others. ### Software Developers & Boat Electronics Hobbyists -There are many boaters who happen to be highly skilled software developers and engineers, who want to build software for themselves and share with others. If you are one of them, Signal K offers you a free, modern and open platform developed by boaters for other boaters like you. Signal K Server features an extensible [plugin framework](https://github.com/SignalK/signalk-server/blob/master/SERVERPLUGINS.md), [web applications](https://github.com/SignalK/signalk-server/blob/master/WEBAPPS.md) as well as a rich set of [REST](https://signalk.org/specification/1.7.0/doc/rest_api.html) and [Streaming APIs](https://signalk.org/specification/1.7.0/doc/streaming_api.html). +There are many boaters who happen to be highly skilled software developers and engineers, who want to build software for themselves and share with others. If you are one of them, Signal K offers you a free, modern and open platform developed by boaters for other boaters like you. Signal K Server features an extensible [plugin framework](./docs/src/develop/plugins/server_plugin.md), [web applications](./docs/src/develop/webapps.md) as well as a rich set of [REST](https://signalk.org/specification/1.7.0/doc/rest_api.html) and [Streaming APIs](https://signalk.org/specification/1.7.0/doc/streaming_api.html). Signal K Server takes care of all the complicated parts of protocol decode, and conversions to and from NMEA2000, NMEA0183 and many more protocols. It can also act as data hub for additional sensors, see the [Signal K SensESP project](https://github.com/SignalK/SensESP) for [ESP32](https://en.wikipedia.org/wiki/ESP32). -Signal K Server makes the data available in JSON format according to the [Signal K standard specification](https://signalk.org/specification/latest/). This allows developers to bypass all the hurdles typically encountered when wanting to implement something for a boat. [Getting started with a plugin](https://github.com/SignalK/signalk-server/blob/master/SERVERPLUGINS.md#getting-started-with-plugin-development) is surprisingly easy. +Signal K Server makes the data available in JSON format according to the [Signal K standard specification](https://signalk.org/specification/latest/). This allows developers to bypass all the hurdles typically encountered when wanting to implement something for a boat. [Getting started with a plugin](./docs/src/develop/plugins/server_plugin.md#getting-started-with-plugin-development) is surprisingly easy. ## Signal K Platform @@ -52,7 +50,9 @@ Signal K is more than just the Signal K Server, it is a comprehensive platform t 2. **Signal K Server**: Software in this GitHub repository and described in this document. Signal K server is a full stack application developed in Node.js. Its back-end multiplexes data from and to NMEA0183, NMEA 2000, Signal K and other marine protocols, as well as WiFi, LAN and Internet, and provides APIs and websockets for access and control. Its front-end provides an extensible web-based application allowing easy configuration and management of server functions and capabilities. 3. **Signal K Plugins and Webapps**: Built using the extensibility of Signal K Server with a plugin framework, allows developers to develop applications that easily integrate with Signal K server, extend its capabilities and publish them through npm. All published plugins become available in all existing Signal K server installations, which provides an easy distribution mechanism. -## Community & support +## Documentation, Community & Support + +[Documentation for Signal K Server](https://demo.signalk.org/documentation). See [Github Discussions](https://github.com/SignalK/signalk/discussions/) and [Slack (chat)](https://signalk-dev.slack.com/). New to Signal K Slack? Then [click here for an invite](https://join.slack.com/t/signalk-dev/shared_invite/zt-1leccop43-KrU7G6yBq9g91KXjZtNg1g). @@ -85,7 +85,7 @@ And an installer for Windows: Another level up, this document explains how to install Signal K Server, as well as its dependencies, on a RaspberryPi that is already running Raspberry Pi OS: -* [Installation on a RaspberryPi](https://github.com/SignalK/signalk-server-node/blob/master/raspberry_pi_installation.md) +* [Installation on a RaspberryPi](./docs/src/installation/raspberry_pi_installation.md) Last, here is how to install the Signal K Server application from NPM: @@ -193,61 +193,19 @@ To enable debugging without going through the Admin UI, see the file `~/.signalk * NMEA0183 sentences: [nmea0183-signalk](https://github.com/SignalK/signalk-parser-nmea0183) * TODO ADD OTHER SUPPORTED PROTOCOLS -## Environment variables - -- `PORT` override the port for http/ws service (default is 3000). -- `SSLPORT` override the port for https/wss service. If defined activates ssl as forced, default protocol (default is 3443). -- `PROTOCOL` override http/https where the server is accessed via https but the server sees http (for example when Heroku handles https termination) -- `EXTERNALPORT` the port used in /signalk response and Bonjour advertisement. Has precedence over configuration file. -- `EXTERNALHOST` the host used in /signalk response and Bonjour advertisement. Has precedence over configuration file. -- `FILEUPLOADSIZELIMIT` override the file upload size limit (default is '10mb'). -- `NMEA0183PORT` override the port for the NMEA 0183 over tcp service (default is 10110). -- `TCPSTREAMPORT` override the port for the Signal K Streaming (deltas) over TCP. -- `TCPSTREAMADDRESS` override the address the Signal K Stream (deltas) over TCP is listening on. -- `DISABLEPLUGINS` disable all plugins so that they can not be enabled (default is false). -- `DEFAULTENABLEDPLUGINS` a comma separated list of plugin ids that are overridden to be enabled by default if no setttings exist. lower preference than `DISABLEPLUGINS`. -- `PLUGINS_WITH_UPDATE_DISABLED` a comma separated list of plugin that will not be updated. -- `SECURITYSTRATEGY` override the security strategy module name. -- `WSCOMPRESSION` compress websocket messages (default is false). -- `MAXSENDBUFFERSIZE` the maximum number of bytes allowed in the server's send buffer of a WebSocket connection. The connection will be terminated if this is exceeded. Guards against slow or dysfunctional clients that can not cope with the message volume (default is 512 * 1024 bytes). -- `SIGNALK_SERVER_IS_UPDATABLE` allows the server to be updated through the GUI even if it is not installed in the standard paths (default is false). if set to true, the server must have been installed with `npm install -g signalk-server`. -- `SIGNALK_DISABLE_SERVER_UPDATES` disables server updates in the GUI (default is false). -- `DEBUG` a comma-separated list of tags for debugging the specified module (For example: signalk-server*,signalk-provider-tcp). Can now be defined directly in the graphical interface. - More help on how to use the debug here: https://www.npmjs.com/package/debug#wildcards -- `IS_IN_DOCKER` used to tell the server it is in Docker and not normally updateable (default is false). -- `NPMREGISTRYTIMEOUT` how long to wait for the registry when retrieving the App Store listing (default is 20s). -- `SECRETKEY` a secret string used to generate an authentication token (the internal default autogenerated is a string of 512 hex chars like 'ef8307a4c7a4bd7...309d947bca3') -- `ALLOW_DEVICE_ACCESS_REQUESTS` used when a device needs to gain access to a secured Signal K server (default is true) (https://signalk.org/specification/1.4.0/doc/access_requests.html). -- `ALLOW_NEW_USER_REGISTRATION` (default is true). -- `ADMINUSER` force a account for admin user (username:password format). -- `PRESERIALCOMMAND` command to run before opening a serial port. -- `MFD_ADDRESS_SCRIPT` command to run to provide a comma separate list of server addresses to advertise to (Navico) MFDs. -- `SIGNALK_NODE_SETTINGS` override the path to the settings file. -- `SIGNALK_NODE_CONFIG_DIR` override the path to find server configuration. Includes all run-time changing content: configuration files, plugins, plugin configuration files, webapps, and so forth. - -## Command line options - -- `-c`: same as env variable `SIGNALK_NODE_SETTINGS` -- `-s`: same as env variable `SIGNALK_NODE_CONFIG_DIR` -- `--sample-nmea0183-data`: starts signalk-server with sample NMEA0183 data. -- `--sample-n2k-data`: starts signalk-server with sample NMEA2000 data. -- `--override-timestamps`: overrides timestamps in the sample NMEA2000 data with current date and time. Doesn't apply nor makes a difference to NMEA0183 sample data. -- `--securityenabled`: one of the ways to enable security. For a fresh install this makes the Admin UI force the user to create an admin account before he/she can continue further into the UI. See [SECURITY.md#enabling-security](https://github.com/SignalK/signalk-server/blob/master/SECURITY.md#enabling-security) for further details. - - ## Development ### Further reading The documents provide more details about developing Webapps or Plugings for Signal K Server, as well as working on the server itself: -* [Contributing to this repo](docs/CONTRIBUTING.md) -* [Server Plugins](SERVERPLUGINS.md) -* [Webapps](WEBAPPS.md) -* [Working with the Course API](WORKING_WITH_COURSE_API.md) -* [Working with the Resources API](WORKING_WITH_RESOURCES_API.md) -* [Resource Provider Plugins](RESOURCE_PROVIDER_PLUGINS.md) -* [Security](SECURITY.md) +* [Contributing to this repo](docs/src/develop/contributing.md) +* [Server Plugins](docs/src/develop/plugins/server_plugin.md) +* [Webapps](docs/src/develop/webapps.md) +* [Working with the Course API](docs/src/develop/rest-api/course_api.md) +* [Working with the Resources API](docs/src/develop/rest-api/resources_api.md) +* [Resource Provider Plugins](docs/src/develop/plugins/resource_provider_plugins.md) +* [Security](docs/src/security.md) ### Install from git diff --git a/SECURITY.md b/SECURITY.md deleted file mode 100644 index 81be6e1a7..000000000 --- a/SECURITY.md +++ /dev/null @@ -1,106 +0,0 @@ -Introduction -======== - -The umbrella term *Security* in Signal K server refers to the difference between running an *unsecured server*, that everybody who has network access to it can access and alter at will, and a *secured server* that has one or more security restrictions enabled. - -The security options are related to -* **authentication**: the users are authenticated, for example but not limited to username & password -* **access control**: based on authentication we can grant / limit access to Signal K data and server configuration -* **secure communications**: is the network traffic protected against eavesdropping by using encryption and can the identity of the server be verified -* **active network services**: which of the server's services/interfaces are configured and active, for example does it allow unsecured read/write over the network - -Enabling Security -======= - -You can tell that a server does not have security turned on from not having `Login` option at the top right corner of the admin UI. - -You can enable security in several ways: -- by accessing the Security settings pages in the admin UI -- starting the server with the `--securityenabled` option -- adding the following section in the settings file - -``` -"security": { - "strategy": "./tokensecurity", - } -``` - -When security is enabled the admin UI will prompt you to create the admin account in the admin UI. - -Security configuration is stored in file called `security.json` which will be located in the configuration directory. - -Disabling Security / Lost Admin Credentials -========== - -**In case you accidentally lose your admin credentials you can remove `security.json` and restart.** - -Access Control -==== - -Access control lists allow fine grained control of access to specific data in SignalK. The acls are a list which allow specifiying controls for specifc contexts and it goes in the security.json file mentioned above. - -The following example defines acls for the self context. It allows anyone to read the paths `"steering.*"`, `"navigation.*"`, `"name"`, `"design.aisShipType"` and allows the admin user permission to write (update) those paths. - -The second entry allows the user _john_ to read any data coming from the `actisense.35` $source. - -The last entry covers all other paths, allowing only the admin user to read and no one can write. - -If there is no match is found for a specific path in the acl list, then permission will be denied to that path. - -``` - "acls": [ - { - "context": "vessels.self", - "resources": [ - { - "paths": ["steering.*", "navigation.*", "name", "design.aisShipType"], - "permissions": [ - { - "subject": "any", - "permission": "read" - }, - { - "subject": "admin", - "permission": "write" - } - ] - }, - { - "sources": [ "actisense.35" ], - "permissions": [ - { - "subject": "john", - "permission": "read" - } - ] - }, - { - "paths": ["*"], - "permissions": [ - { - "subject": "admin", - "permission": "read" - } - ] - } - ] - } - ] - ``` - -Active network services -===== - -Signal K server's main networks services are -- the *primary Signal K http / WebSocket interface*, with options to use TLS encryption and authentication (read/write) -- *NMEA0183 data over TCP* on port 10110 (read only) -- *Signal K over TCP* on port 8375 (read/write) - -In addition the user may configure any number of TCP, UDP and Websocket connections, some of which allow write access to the server. - -The security implication of these connections is that with no security options turned on *computers that have network access to the server have both read and write access to practically all of its data and settings*. - -People often dismiss local network access by saying that their boat's local network is secure enough. But one very common scenario is connecting your SK server, for example a Raspberry Pi, as a client to a marina wifi. Many wifi networks allow communication between all connected computers. Your the server will be advertising its services over MDNS to all other connected computers. If there is a manually configured connection for NMEA0183 over UDP other computers broadcasting data can easily write data to the server. - -NMEA0183 connections over TCP and UDP are inherently unsafe: there are no options for authentication and secure communication. In comparison Signal K over TLS and HTTP / WebSockets can provide secure, authentication read and write access to your data. - diff --git a/SERVERPLUGINS.md b/SERVERPLUGINS.md deleted file mode 100644 index 3c419003d..000000000 --- a/SERVERPLUGINS.md +++ /dev/null @@ -1,967 +0,0 @@ -# Server plugins - -![image](https://user-images.githubusercontent.com/1049678/35973231-5d89ad56-0cdd-11e8-9d89-42313b468520.png) -![image](https://user-images.githubusercontent.com/1049678/35973284-7e1c319c-0cdd-11e8-918f-53bad7a3b706.png) - -## Overview - -Signal K Node server plugins are components that run within the server and add some functionality to the server. You can configure them via the admin UI. - -Plugins -- are installed, activated/disabled and configured from the server admin UI -- start in disabled state - you need to enable them after install -- can be webapps as well: a webapp's `/public/` directory is mounted under server's root under module id http://yourserver/moduleid -- are npm modules that become available to the server when they are published in the [npm repository](https://www.npmjs.com/) with the `signalk-node-server-plugin` keyword - - -The plugin module must export a single `function(app)` that must return an object with the following: -- function `start(configuration, restartPlugin)`: This function will be called when the plugin is activated or when the server starts and the plugin is activated. Here you'll setup your plugin, subscribe to streams, connect to devices or start async loops to fetch data. -- function `stop()`: This function is called when the plugin is deactivated. Here you typically unsubscribe from streams, stop loops that fetch data or close devices. -- property or function `schema`: The object returned from this property or function defines the configuration of the plugin and is used to generate the configuration user interface for the plugin. The value can be an object or `Promise`. -- (optional) property or function `uiSchema`: The returned structure defines additional UI components or effects. The value can be an object or `Promise`. - -Whan a plugin's configuration is changed the server will first call `stop` to stop the plugin and then `start` with the new configuration data. - -## Getting Started with Plugin Development - -To get started with SignalK plugin development, you can follow this guide. - -_Note: For plugins acting as a provider for one or more of the SignalK resource types listed in the specification (`routes`, `waypoints`, `notes`, `regions` or `charts`) please refer to __[RESOURCE_PROVIDER_PLUGINS.md](./RESOURCE_PROVIDER_PLUGINS.md)__ for additional details._ - -### Project setup - -First, create a new directory and initialize a new module: - -``` -$ mkdir my-signalk-plugin -$ cd my-signalk-plugin -$ npm init -``` - -Then, add `signalk-node-server-plugin` keyword to `package.json`, so it looks something like this: - -```javascript -{ - "name": "my-signalk-plugin", - "version": "1.0.0", - "description": "My great signalk plugin", - "main": "index.js", - "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" - }, - "author": "", - "license": "ISC", - "keywords": [ - "signalk-node-server-plugin", - "signalk-category-ais" - ] -} -``` - -You should also include at least one App Store category under keywords in your `package.json`. The available categories are: - -- signalk-category-nmea-2000 -- signalk-category-nmea-0183 -- signalk-category-instruments -- signalk-category-chart-plotters -- signalk-category-hardware -- signalk-category-ais -- signalk-category-notifications -- signalk-category-digital-switching -- signalk-category-utility -- signalk-category-cloud -- signalk-category-weather -- signalk-category-deprecated -- signalk-category-hidden (won't show on the App Store) - -Plugins are normally installed in `node_modules` inside SignalK server's configuration directory (`$HOME/.signalk` by default). For development we can use `npm link` to link your module into the modules directory of the server. This way you can develop outside of the source tree of the server and publish your plugin separately. - -First, prepare your plugin for linking: -``` -$ npm link -... - -/usr/local/lib/node_modules/my-signalk-plugin -> /home/me/dev/my-signalk-plugin -``` - -Then from your SignalK server's configuration directory (`$HOME/.signalk/` by default), you link your plugin. - -``` -$ cd ~/.signalk -$ npm link my-signalk-plugin -... - -/home/me/.signalk/node_modules/my-signalk-plugin -> /usr/local/lib/node_modules/my-signalk-plugin -> /home/me/dev/my-signalk-plugin -``` - -### Plugin skeleton - -In your module directory, create `index.js` with the following content: - -```javascript -module.exports = function (app) { - var plugin = {}; - - plugin.id = 'my-signalk-plugin'; - plugin.name = 'My Great Plugin'; - plugin.description = 'Plugin that does stuff'; - - plugin.start = function (options, restartPlugin) { - // Here we put our plugin logic - app.debug('Plugin started'); - }; - - plugin.stop = function () { - // Here we put logic we need when the plugin stops - app.debug('Plugin stopped'); - }; - - plugin.schema = { - // The plugin schema - }; - - return plugin; -}; - -``` - -You should be able to restart the server now. If you set the `DEBUG` environment variable, you can get debug output about the plugin loading process: - -``` -$ DEBUG=signalk:interfaces:plugins signalk-server -``` - -And you should see something like: -``` -signalk:interfaces:plugins Registering plugin my-signalk-plugin +0ms -signalk:interfaces:plugins Could not find options for plugin my-signalk-plugin, returning empty options: +2ms -``` - -After everything loads well, you can debug your own plugin by running: - -``` -$ DEBUG=my-signalk-plugin signalk-server -``` - -You should be able to now enable your plugin in the admin UI (although it doesn't do anything yet). - -You should see something like this in your console: -``` -my-signalk-plugin Plugin stopped +0ms -my-signalk-plugin Plugin started +2ms -``` - -For development purposes, it's often nice to have some mocked data. SignalK comes with a synthesized NMEA2000 data set that can be used as sample data. You can enable this by adding `--sample-n2k-data` to the command line: - -``` -$ DEBUG=my-signalk-plugin signalk-server --sample-n2k-data -``` - -### Deltas - -Most of the time your plugin wants to either process data that the server gathers (e.g. data that is coming from a NMEA 2000 bus or another source) or generate data for the server to consume (e.g. data you capture from a device or the internet). In both cases you'll often work with *deltas*; data structures that describe a change in Signal K's internal data model. - -The [Signal K Delta Specification](http://signalk.org/specification/1.3.0/doc/data_model.html#delta-format) defines deltas that are used to send updates to clients. The delta format describes messages that may contain 1 or more updates - it is an envelope format and you can stuff more than one thing in a message. - -However most of the time a data consumer like a plugin is interested in updates to just a few particular data items. This is why the server's plugin API uses *denormalized deltas* to allow handling data items independently. When you handle the individual items separately you do not need to go through the delta's envelope format to find the item that you have interest in. - -Note that deltas contain absolute values and not a change in the value itself, but denote a change in the data model. - -Denormalized deltas have the following structure: -```javascript - { - path: ..., - value: ..., - context: ..., - source: ..., - $source: ..., - timestamp: ... - } -``` - -The following delta shows a change in `navigation.position` and contains the updated `longitude` and `latitude` for this specific vessel: -```javascript -{ - path: 'navigation.position', - value: { longitude: 24.7366117, latitude: 59.72493 }, - context: 'vessels.urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60', - source: { - label: 'n2k-sample-data', - type: 'NMEA2000', - pgn: 129039, - src: '43' - }, - '$source': 'n2k-sample-data.43', - timestamp: '2014-08-15T19:00:02.392Z' -} -``` -## Paths -To be able to access data in SignalK's data model, there is the notion of *paths*. Paths are like keys in a hashmap or dictionary, but traverse branches. - -Say we have the following data: -```javascript -{ - vessels: { - ... - }, - shore: { - basestations: { - 'urn:mrn:imo:mmsi:2766140': [Object], - 'urn:mrn:imo:mmsi:2766160': [Object], - 'urn:mrn:imo:mmsi:2300048': [Object], - 'urn:mrn:imo:mmsi:2300047': [Object] - } - } -} -``` -We can access all the base stations with the path `shore.basestations` or even a single base station through the path `shore.basestations.urn:mrn:imo:mmsi:2766140`. - -## Contexts -The SignalK data model also provides different contexts. In some cases you want your plugin to only process data that is coming from your own vessel, while in other cases you want to receive data from other contexts (e.g. other vessels, base stations ashore, etc). Contexts allow you to only receive deltas for parts of the data model. - -SignalK server has a shortcut to your own vessels' context through `vessels.self`. Sometimes you can configure the context (like in the subscription example above), so you don't have to filter out the deltas that are coming from your own vessel. - -In other cases you might want to use a specific context (e.g. when you want only deltas from another vessel) and sometimes you want all the deltas, regardless of their context. In the latter case, you can use `*`. - -### Processing data from the server -You can get the value of a path or subscribe to updates for one or more paths (e.g. `navigation.datetime`). - -Since paths are hierarchical, paths can contain wildcards (like `*`). - -Let's start with getting the value of `uuid` inside our `plugin.start` function: - -```javascript -let value = app.getSelfPath('uuid'); -app.debug(value); // Should output something like urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60 -``` - -Secondly, we can subscribe to a stream of updates (deltas) for a specific path. We can create the subscription inside our `plugin.start` function: - -```javascript -var unsubscribes = []; - -plugin.start = function (options, restartPlugin) { - app.debug('Plugin started'); - let localSubscription = { - context: '*', // Get data for all contexts - subscribe: [{ - path: '*', // Get all paths - period: 5000 // Every 5000ms - }] - }; - - app.subscriptionmanager.subscribe( - localSubscription, - unsubscribes, - subscriptionError => { - app.error('Error:' + subscriptionError); - }, - delta => { - delta.updates.forEach(u => { - app.debug(u); - }); - } - ); -}; -``` - -Since we would like to unsubscribe from updates when the plugin stops, we create an empty array that called `unsubscribes`. This array will be populated with unsubscribe functions by the `subscriptionmanager`. When the plugin stops, we can call these functions. - -We create a subscription definition and pass that to `app.subscriptionmanager.subscribe()` as the first argument. -The second argument takes our `unsubscribes` array and the third argument takes a function that will be called when there's an error. The last argument should be a function that handles the delta update; in our case we just debug print the output. - -Now we can add the following to our `plugin.stop` function: - -```javascript -plugin.stop = function () { - unsubscribes.forEach(f => f()); - unsubscribes = []; -}; -``` -Here we loop though all the unsubscribe functions for our subscriptions and call each function. - -Now restart the server and you should see deltas being output in your console. - -## Sending data out from a plugin - -A SignalK plugin can not only read deltas, but can also send data. The following examples show how you can emit NMEA 2000 data. - -### Actisense serial format - -To send a message in the Actisense serial format, simply use `app.emit()` in the following way: -``` javascript - app.emit( - 'nmea2000out', - '2017-04-15T14:57:58.468Z,0,262384,0,0,14,01,0e,00,88,b6,02,00,00,00,00,00,a2,08,00'); -``` - -### Canboat JSON format - -To send data in `canboat` JSON format, use `app.emit()` in the following way: - -```javascript - app.emit('nmea2000JsonOut', { - pgn: 130306, - 'Wind Speed': speed, - 'Wind Angle': angle < 0 ? angle + Math.PI*2 : angle, - 'Reference': "Apparent" - }); -``` - -### Sending a message on NMEA2000 startup - -If you need to send an N2K message out at startup, e.g to get current state from a device: - -```javascript - app.on('nmea2000OutAvailable', () => { - app.emit( - 'nmea2000out', - '2017-04-15T14:57:58.468Z,2,6,126720,%s,%s,4,a3,99,01,00'); - }); -``` - -### Outbound data indication - -If you send data out, you might want to show that also on Server Admin UI, at Connection activity section. - -```javascript - setImmediate(() => - app.emit('connectionwrite', { providerId: plugin.id }) - ) -``` - -## Inbound & Outbound data indicators on Server Admin UI - -Connection activity section on Server Admin UI is showing traffic status of Signal K connections and plugings. There are inbound and outbound icons in front of connection or plugins title. -Icon has three statuses: -- light blue until traffic is detected -- pulsating dark blue when traffic is detected -- solid dark blue when no active traffic is detected - -Inbound data: deltas/s
-Outbound data: msg/s - -Inbound type is always deltas, data coming into the server. Outbound data can vary and depends on plugins structures, generically saying messages (deltas, nmeas sentences, etc...) - -## Schema - -Every plugin defines a `schema` that is used by the server to render the plugins' configuration screen. Within the module, `schema` can either be a property or a function that returns a [JSON Schema](http://json-schema.org/) structure describing the plugin's configuration data. - -For example: -```javascript - plugin.schema = { - type: 'object', - required: ['some_string', 'some_other_number'], - properties: { - some_string: { - type: 'string', - title: 'Some string that the plugin needs' - }, - some_number: { - type: 'number', - title: 'Some number that the plugin needs', - default: 60 - }, - some_other_number: { - type: 'number', - title: 'Some other number that the plugin needs', - default: 5 - } - } - }; -``` -JSON Schema approach works reasonably well for simple to medium complex configuration data. Some JSON schema constructs are not supported ([details](https://github.com/peterkelly/react-jsonschema-form-bs4/blob/v1.7.1-bs4/docs/index.md#json-schema-supporting-status)). The server supports also [custom plugin configuration components](https://github.com/SignalK/signalk-server/blob/master/WEBAPPS.md), bypassing the automatic configuration format generation. - -The configuration is passed in to the `plugin.start` function as the first argument: - -```javascript -plugin.start = function (options, restartPlugin) { - // options contains the plugin configuration - ... -} -``` - -### UI Schema -The `uiSchema` value is used by the user interface to provide information on how the configuration form should rendered and should conform [the uiSchema object](https://github.com/mozilla-services/react-jsonschema-form#the-uischema-object). - -In the Admin UI, you can make sections of your configuration collapsible. E.g. to make all data in an object called 'myObject' collapsible: -```javascript -uiSchema['myObject'] = { - 'ui:field': 'collapsible', - collapse: { - field: 'ObjectField', - wrapClassName: 'panel-group' -} -``` - -For more information, see [react-jsonschema-form-extras](https://github.com/RxNT/react-jsonschema-form-extras#collapsible-fields-collapsible) - - -## Restarting a plugin -As you might have seen, the second argument of `plugin.start` is called `pluginRestart`. This is a function that is passed to be able to restart the plugin. A plugin can call `restartPlugin()` to restart itself. - -## Removing a plugin - -If you have have installed the server from npm and have used the setup script the plugins that you have installed yourself are installed under `~/.signalk/node_modules` and listed in `~/.signalk/package.json`. If you want to remove a plugin you should remove it from `package.json` and then either run `npm prune` in `~/.signalk/` directory or wipe `~/.signalk/node_modules` and run `npm install` in `~/.signalk/`. - -Plugin settings are stored in `~/.signalk/plugin-config-data/` and can just delete the settings file for the plugin you are removing. - -## Making a plugin enabled by default -If your plugin does not require any initial configuration, you can enable it by default. Add the following property to your `package.json`: - -```json - "signalk-plugin-enabled-by-default": true -``` - -## Plugin configuration files - -A plugin's configuration data is saved at `SIGNALK_NODE_CONFIG_DIR/plugin-config-data/.json`. You can disable a plugin by removing its configuration file. - -## Logging - -The plugin configuration screen has an option for turning on logging per plugin. Enabling logging will cause any deltas sent by the plugin to be logged in the server's data log. - -## Examples - -Some easier to understand examples of SignalK plugins are: -- [set-system-time](https://github.com/SignalK/set-system-time/blob/master/index.js) -- [Ais Reporter](https://github.com/SignalK/aisreporter/issues) - -# Server API for plugins - -Internally, SignalK server builds a full data model. Plugins can access the server's delta stream (updates) and full model and provide additional data as deltas using the following functions. - -### `app.handleMessage(pluginId, delta, skVersion = 'v1')` - -Allows the plugin to publish deltas to the server. These deltas are handled as any incoming deltas. - -```javascript -app.handleMessage('my-signalk-plugin', { - updates: [ - { - values: [ - { - path: 'navigation.courseOverGroundTrue', - value: Math.PI - } - ] - } - ] -}) -``` - -Deltas that use Signal K V2 paths (like the [Course API](http://localhost:3000/admin/openapi/?urls.primaryName=course) paths) should call `handleMessage` with the optional 3rd parameter set to `v2`. This prevents V2 API data getting mixed in V1 paths' data in Full model & the v1 http API. If you don't know that your data is V2 API data you can omit the third parameter, as the default is V1. - -### `app.getSelfPath(path)` - -Get a Signal K path for the `vessels.self`'s full data model. - -```javascript -let uuid = app.getSelfPath('uuid'); -app.debug(uuid); // Should output something like urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60 -``` - -### `app.getPath(path)` - -Get a Signal K path starting from the root of the full data model. - -```javascript -let baseStations = app.getPath('shore.basestations'); - -// baseStations: - -{ - 'urn:mrn:imo:mmsi:2766140': { - url: 'basestations', - navigation: { position: [Object] }, - mmsi: '2766140' - }, - 'urn:mrn:imo:mmsi:2766160': { - url: 'basestations', - navigation: { position: [Object] }, - mmsi: '2766160' - }, - ... -} -``` - -### `app.streambundle.getSelfBus(path)` - -Get a [Bacon JS](https://baconjs.github.io/) stream for `vessels.self`'s Signal K path. `path` argument is optional. If it is not provided the returned stream -produces values for all paths. Stream values are objects with structure - -```javascript - { - path: ..., - value: ..., - context: ..., - source: ..., - $source: ..., - timestamp: ... - } -``` - -For example: -```javascript -app.streambundle - .getSelfBus('navigation.position') - .forEach(pos => app.debug(pos)); -``` - -Outputs: -```javascript -... -{ - path: 'navigation.position', - value: { longitude: 24.7366117, latitude: 59.72493 }, - context: 'vessels.urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60', - source: { - label: 'n2k-sample-data', - type: 'NMEA2000', - pgn: 129039, - src: '43' - }, - '$source': 'n2k-sample-data.43', - timestamp: '2014-08-15T19:00:02.392Z' -} -{ - path: 'navigation.position', - value: { longitude: 24.7366208, latitude: 59.7249198 }, - context: 'vessels.urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60', - source: { - label: 'n2k-sample-data', - type: 'NMEA2000', - pgn: 129025, - src: '160' - }, - '$source': 'n2k-sample-data.160', - timestamp: '2014-08-15T19:00:02.544Z' -} -... -``` - -### `app.streambundle.getSelfStream(path)` - -Get a [Bacon JS](https://baconjs.github.io/) stream for `vessels.self`'s Signal K path. `path` argument is optional. If it is not provided the returned stream -produces values for all paths. This is similar to `app.streambundle.getSelfBus(path)`, but the stream values are the `value` properties from incoming deltas. - -```javascript -app.streambundle - .getSelfStream('navigation.position') - .forEach(pos => app.debug(pos)); -``` - -Outputs: -``` - my-signalk-plugin { longitude: 24.736677, latitude: 59.7250108 } +600ms - my-signalk-plugin { longitude: 24.736645, latitude: 59.7249883 } +321ms - my-signalk-plugin { longitude: 24.7366563, latitude: 59.7249807 } +174ms - my-signalk-plugin { longitude: 24.7366563, latitude: 59.724980699999996 } +503ms -``` - -### `app.streambundle.getBus(path)` - -Get a [Bacon JS](https://baconjs.github.io/) stream for a Signal K path that will stream values from any context. `path` argument is optional. If it is not provided the returned stream -produces values for all paths. Stream values are objects as in `app.streambundle.getSelfBus(path)`. - -### `app.streambundle.getAvailablePaths()` - -Get a list of paths currently available in the server - -```javascript -[ - "navigation.speedOverGround", - "navigation.courseOverGroundTrue", - "navigation.courseGreatCircle.nextPoint.position", - "navigation.position", - "navigation.gnss.antennaAltitude", - "navigation.gnss.satellites", - "navigation.gnss.horizontalDilution", - "navigation.gnss.positionDilution", - "navigation.gnss.geoidalSeparation", - "navigation.gnss.type","navigation.gnss.methodQuality", - "navigation.gnss.integrity", - "navigation.magneticVariation", - ... -] -``` - -### `app.error(message)` - -Report errors in a human-oriented message. Currently just logs the message, but in the future error messages hopefully will show up in the admin UI. - -### `app.debug(...)` - -Log debug messages. This is the debug method from the [debug module](https://www.npmjs.com/package/debug). The npm module name is used as the debug name. - -`app.debug()` can take any type and will serialize it before outputting. - -*Do not use `debug` directly*. Using the debug function provided by the server makes sure that the plugin taps into the server's debug logging system, including the helper switches in Admin UI's Server Log page. - -### `app.savePluginOptions(options, callback)` - -Save changes to the plugin's options. - -```javascript -var options = { - myConfigValue = 'Something the plugin calculated' -}; - -app.savePluginOptions(options, () => {app.debug('Plugin options saved')}); - -``` - -### `app.readPluginOptions()` - -Read plugin options from disk. - -```javascript -var options = app.readPluginOptions(); -``` - -### `app.getDataDirPath()` - -Returns the full path of the directory where the plugin can persist its internal data, like data files. - -Example use: -```javascript -var myFile = require('path').join(app.getDataDirPath(), 'somefile.ext') -``` - -### `app.registerPutHandler(context, path, callback, source)` - -If a plugin wants to respond to [`PUT`](http://signalk.org/specification/1.3.0/doc/put.html) requests for a specific path, it can register an action handler. - -The action handler can handle the request synchronously or asynchronously. - -The passed callback should be a function taking the following arguments: `(context, path, value, callback)` - -For synchronous actions the handler must return a value describing the response of the request: for example - -```javascript -{ - state: 'COMPLETED', - statusCode: 200 -} -``` - - or - - ```javascript -{ - state:'COMPLETED', - statusCode: 400, - message:'Some Error Message' -} - ``` - - The `statusCode` value can be any valid HTTP response code. - -For asynchronous actions that may take considerable time and where the requester should not be kept waiting for the result the handler must return - -```javascript -{ state: 'PENDING' } -``` - -When the action is finished the handler should call the `callback` function with the result with - -```javascript -callback({ state: 'COMPLETED', statusCode: 200 }) -``` -or - -```javascript -callback({ - state:'COMPLETED', - statusCode: 400, - message:'Some Error Message' -}) -``` - -Synchronous example: -```javascript -function myActionHandler(context, path, value, callback) { - if(doSomething(context, path, value)){ - return { state: 'COMPLETED', statusCode: 200 }; - } else { - return { state: 'COMPLETED', statusCode: 400 }; - } -} - -plugin.start = function(options) { - app.registerPutHandler('vessels.self', 'some.path', myActionHandler, 'somesource.1'); -} -``` - -Asynchronous example: -```javascript -function myActionHandler(context, path, value, callback) { - doSomethingAsync(context, path, value, (result) =>{ - if(result) { - callback({ state: 'COMPLETED', result: 200 }) - } else { - callback({ state: 'COMPLETED', result: 400 }) - } - }); - return { state: 'PENDING' }; -} - -plugin.start = function(options) { - app.registerPutHandler('vessels.self', 'some.path', myActionHandler); -} -``` - -### `app.registerDeltaInputHandler ((delta, next) => ...)` - -Register a function to intercept all delta messages *before* they are processed by the server. The plugin callback should call `next(delta)` with a modified delta if it wants to alter the incoming delta or call `next` with the original delta to process it normally. Not calling `next` will drop the incoming delta and will only show in delta statistics. -Other, non-delta messages produced by provider pipe elements are emitted normally. - -```javascript -app.registerDeltaInputHandler((delta, next) => { - delta.updates.forEach(update => { - update.values.forEach(pathValue => { - if(pathValue.startsWith("foo")) { - pathValue.path = "bar" - } - }) - }) - next(delta) -}) -``` - -### `app.registerResourceProvider(resourceProvider)` - -See [`RESOURCE_PROVIDER_PLUGINS`](./RESOURCE_PROVIDER_PLUGINS.md) for details. - ---- -### `app.resourcesApi.getResource(resource_type, resource_id, provider_id?)` - -Retrieve data for the supplied SignalK resource_type and resource_id. - -_Note: Requires a registered Resource Provider for the supplied `resource_type`._ - - - `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ - or user defined resource types. - - - `resource_id`: The id of the resource to retrieve _(e.g. `ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a`)_ - - - `provider_id` (optional): The id of the Resource Provider plugin to specifically use. Can be specified when more than one provider has been registered for a reource type._(e.g. `resources-provider`)_ - -- returns: `Promise<{[key: string]: any}>` - -_Example:_ -```javascript -app.resourcesApi.getResource( - 'routes', - 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a' -).then (data => { - // route data - console.log(data); - ... -}).catch (error) { - // handle error - console.log(error.message); - ... -} -``` - -### `app.resourcesApi.setResource(resource_type, resource_id, resource_data, provider_id?)` - -Create / update value of the resource with the supplied SignalK resource_type and resource_id. - -_Note: Requires a registered Resource Provider for the supplied `resource_type`._ - - - `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ - or user defined resource types. - - - `resource_id`: The id of the resource to retrieve _(e.g. `ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a`)_ - - - `resource_data`: A complete and valid resource record. - - - `provider_id` (optional): The id of the Resource Provider plugin to specifically use. Can be specified when more than one provider has been registered for a reource type._(e.g. `resources-provider`)_ - -- returns: `Promise` - -_Example:_ -```javascript -app.resourcesApi.setResource( - 'waypoints', - 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a', - { - "position": {"longitude": 138.5, "latitude": -38.6}, - "feature": { - "type":"Feature", - "geometry": { - "type": "Point", - "coordinates": [138.5, -38.6] - }, - "properties":{} - } - } -).then ( () => { - // success - ... -}).catch (error) { - // handle error - console.log(error.message); - ... -} -``` - -### `app.resourcesApi.deleteResource(resource_type, resource_id, provider_id?)` - -Delete the resource with the supplied SignalK resource_type and resource_id. - -_Note: Requires a registered Resource Provider for the supplied `resource_type`._ - -- `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ -or user defined resource types. - -- `resource_id`: The id of the resource to retrieve _(e.g. `ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a`)_ - -- `provider_id` (optional): The id of the Resource Provider plugin to specifically use. Can be specified when more than one provider has been registered for a reource type._(e.g. `resources-provider`)_ - -- returns: `Promise` - -_Example:_ -```javascript -app.resourcesApi.deleteResource( - 'notes', - 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a' -).then ( () => { - // success - ... -}).catch (error) { - // handle error - console.log(error.message); - ... -} -``` - -### `app.resourcesApi.listResources(resource_type, params, provider_id?)` - -Retrieve data for the supplied SignalK resource_type and resource_id. - -_Note: Requires a registered Resource Provider for the supplied `resource_type`._ - - - `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ - or user defined resource types. - - - `params`: Object contining `key | value` pairs repesenting the parameters by which to filter the returned entries. - - - `provider_id` (optional): The id of the Resource Provider plugin to specifically use. Can be specified when more than one provider has been registered for a reource type._(e.g. `resources-provider`)_ - - __Note: The registered Resource Provider must support the supplied parameters for results to be filtered.__ - -- returns: `Promise<{[key: string]: any}>` - -_Example:_ -```javascript -app.resourcesApi.listResources( - 'waypoints', - {region: 'fishing_zone'} -).then (data => { - // success - console.log(data); - ... -}).catch (error) { - // handle error - console.log(error.message); - ... -} -``` - - ---- -### `app.setPluginStatus(msg)` - -Set the current status of the plugin. The `msg` should be a short message describing the current status of the plugin and will be displayed in the plugin configuration UI and the Dashboard. - -```javascript -app.setPluginStatus('Initializing'); -// Do something -app.setPluginStatus('Done initializing'); -``` - -Use this instead of deprecated `setProviderStatus` - -### `app.setPluginError(msg)` - -Set the current error status of the plugin. The `msg` should be a short message describing the current status of the plugin and will be displayed in the plugin configuration UI and the Dashboard. - -```javascript -app.setPluginError('Error connecting to database'); -``` - -Use this instead of deprecated `setProviderError` - -### `app.getSerialPorts() => Promise` - -This returs a Promise which will resolve to a [Ports](src/serialports.ts#21) object which contains information about the serial ports available on the machine. - - -### PropertyValues -``` -app.emitPropertyValue: (name: string, value: any) => void -onPropertyValues: ( - name: string, - callback: (propValuesHistory: PropertyValue[]) => void -) => Unsubscribe -``` -PropertyValues are a mechanism for passing configuration type values between different parties such as plugins and input connections running in the server process. - -A plugin can *emit* values and register to listen for others emitting them. The difference between the PropertyValues mechanism and Event Emitters in NodeJs is that when you call `onPropertyValues` the callback will get immdediately called with an array of all previous values for the property name, starting with the initial value of `undefined`. If nothing has emitted any values for the property name the callback will be called with a value of `undefined`. - -A PropertyValue has the following structure: -``` -interface PropertyValue { - timestamp: number // millis - setter: string // plugin id, server, provider id - name: string - value: any -} -``` - -Note that the value can be also a function. - -This mechanism allows plugins to _offer_ extensions via _"Well Known Properties"_, for example -- additional [NMEA0183 sentence parsers for custom sentences](https://github.com/SignalK/nmea0183-signalk/pull/193) via `nmea0183sentenceParser` -- additional PGN definitions for propietary or custom PGNs - -Code handling incoming PropertyValues should be fully reactive: even if all emitters emit during their startup there is no defined load / startup order and plugins may emit when activated and started. This means that depending on a PropertyValue being there when your code starts or arriving after your code has started is not possible. - -PropertyValues is not meant for data passing on a regular basis, as the total history makes it a potential memory leak. There is a safeguard against accidentally emitting regularly with an upper bound for values per property name. New values will be ignored if it is reached and emits logged as errors. - - -### Exposing custom HTTP paths & OpenApi - -If a plugin has a function called `registerWithRouter(router)` (like the plugin's `start` and `stop` functions) it will be called with an Express router as the parameter during plugin startup. The router will be mounted at `/plugins/` and you can use standard Express `.get` `.post` `.use` etc to add HTTP path handlers. Note that `GET /plugins/` and `POST /plugins//configure` are reserved by server (see below). - -Express does not have a public API for deregistering subrouters, so `stop` does not do anything to the router. - -**Consider seriously providing an OpenApi description** for your plugin's API. This promotes further cooperation with other plugin/webapp authors. One way we can work together is by fleshing out new APIs within a plugin and then merge them in the Signal K specification for more cooperation. - -Implement `getOpenApi` in your plugin to expose your OpenApi to be included in the server's OpenApi UI tooling. The plugin's OpenApi description either not include `servers` property at all or specify only the path in the url. The server will provide servers if it is missing and relative, path only server urls work best in different environments. See [testplugin](https://github.com/SignalK/signalk-server/tree/b82477e63ebdc14878164ce1ed3aedd80c5a8b0c/test/plugin-test-config/node_modules/testplugin) for an example. - -## Plugin configuration HTTP API - -### `GET /plugins/` - -Get a list of installed plugins and their configuration data. - -### `GET /plugins/` - -Get information from an installed plugin. - -Example result: -```json -{ - "enabled": false, - "id": "marinetrafficreporter", - "name": "Marine Traffic Reporter" -} -``` - -### `POST /plugins//configure` - -Save configuration data for a plugin. Stops and starts the plugin as a side effect. diff --git a/Seatalk(GPIO).md b/Seatalk(GPIO).md deleted file mode 100644 index 64cdc6746..000000000 --- a/Seatalk(GPIO).md +++ /dev/null @@ -1,56 +0,0 @@ -## Seatalk(GPIO) - -### 1. Intro - -The connection with ”Input type” => ”Seatalk(GPIO)” supports the function to receive, via simple DIY hardware and one of the GPIO:s on the Raspberry, Raymarine Seatalk 1(ST1) data and convert it to SignalK delta. This information can then be forwarded to a NMEA 0183 or NMEA 2000 network with appropriate hardware and plugins. A guide to SeaTalk is found [here](http://boatprojects.blogspot.com/2012/12/beginners-guide-to-raymarines-seatalk.html). - -Original idea is picked [from Thomas](https://github.com/Thomas-GeDaD/Seatalk1-Raspi-reader) and a circuit update with ideas [from Marco](https://github.com/marcobergman/seatalk_convert). - -### 2. Hardware - -![‎ST1_opto](https://github.com/SignalK/signalk-server/assets/16189982/da0ff2fa-7798-40d7-b61f-67634c202ee5) - -The circuit is referring to [this optocoupler board](https://www.amazon.com/ARCELI-Optocoupler-Isolation-Converter-Photoelectric/dp/B07M78S8LB/ref=sr_1_2?dchild=1&keywords=pc817+optocoupler&qid=1593516071&sr=8-2) but a similar product can of course be used. The LED in the circuit will flicker when there is ST1 traffic. - -Choosing an optocoupler as the hardware interface is a smart way to avoid ground loops and creats electrical isolation from hazardous voltages. The setup above will not invert the signal. - -Do You want it simple ? The simplest possible interface solution is according to the picture below. You use a small signal NPN transistor which shifts the DC level, from 12 V DC to 3,3 V DC, and inverts the signal. - -![ST1_Tr](https://user-images.githubusercontent.com/16189982/88704045-d6fe6e00-d10d-11ea-8f83-cb765e4c65f3.jpeg) - -### 3. Software - -Due the DYI approach and that this function have limited users You have, before configuring the actual connection, to install some software manually in a terminal window. -Start with an update and then the software install - - sudo apt-get update && sudo apt-get install pigpio python-pigpio python3-pigpio - -The connection relies on a [daemon](http://abyz.me.uk/rpi/pigpio/) which is enabled and started via a systemd service. Install with - - sudo systemctl enable pigpiod && sudo systemctl restart pigpiod - -Could be checked with - - sudo systemctl status pigpiod - -Please note that pigpio deamon provides, by default, a socket interface on port 8888, which could conflict with other software You have installed. If You want to move the socket interface to another port You have to change the [pigpiod.service file](http://abyz.me.uk/rpi/pigpio/pigpiod.html) with the -p option, and the [Python program](http://abyz.me.uk/rpi/pigpio/python.html#pigpio.pi). So maybe it's easier to move the conflicting program ? - -Now go on add a connection for the optocoupler setup, in the admin GUI, with type ”Seatalk(GPIO)” according to the picture. - - - -Select which pin, one of the green ones in the picture below. Invert "YES" is used if You have a different hardware interface which is inverting the input signal. - -![GPIO](https://user-images.githubusercontent.com/16189982/86477812-8469a600-bd49-11ea-8e55-4ee4400a2c17.png) - -Restart the SignalK server and then use the ”Data Browser” in the admin GUI to confirm that You are receiving the SK data - -If You are in doubt, if there is data available at the selected GPIO, You can download a program - - wget https://raw.githubusercontent.com/MatsA/seatalk1-to-NMEA0183/master/STALK_read.py - -change the setup in the beginning of program and execute it with - - sudo python STALK_read.py - -If You succed You will se the ST1 sentences roll in. diff --git a/WEBAPPS.md b/WEBAPPS.md deleted file mode 100644 index 294ead8c1..000000000 --- a/WEBAPPS.md +++ /dev/null @@ -1,85 +0,0 @@ -# Webapps and components - -## Introduction - -There are four ways to add web-based user interfaces to the Signal K Server: - -- Standalone Webapps -- Embedded Webapps -- Embedded Plugin Configuration Forms -- Embedded Compontents - -**Standalone Webapps** are Webapps that can be installed on the server via Appstore. The server provides a list of installed webapps. Once you navigate to them the server admin UI disappears and the webapp controls the whole page (browser window / tab). - -**Embedded Webapps** are installed and listed like Standalone Webapps, but they open **embedded in the server admin UI**, leaving the header and footer available so that the user can perform login, restart server and use the admin UI's sidebar to navigate to a different part of the admin UI. - -![vesselpositions](img/vesselpositions.png?raw=true "Vesselpositions Embedded Webapp") - -**Embedded Plugin Configuration Forms** are related to server plugins. A plugin provides a schema for the configuration data it uses. The server uses the schema - a description of the structure of the data used to configure the plugin - to generate configuration forms for the installed plugins. The generated form is often lackin in usability due to it being totally generic. To address this a plugin can provide its own **Configuration Form** that the server embeds within the Plugin Configuration of the server admin UI. - -![calibration](img/calibration.png?raw=true "Calibration plugin configuration form") - -**Embedded Components** are individual UI components provided by a plugin or a webapp. They are currently available at the bottom of the Webapps page of the admin UI. The idea with embedding components would be to allow a plugin to add individual components to different parts of the server, but this is more an idea than a fully implemented feature at this stage. - -## Webapp/Component Structure - -All different webapps (and server plugins) are installed with npm, from npm registry or for example from your own Github repository. Private plugins need not be published to npm - see the documentation for [npm install](https://docs.npmjs.com/cli/v6/commands/npm-install) for the exact details. Only webapps that are relevant for all users should be published in npm to be available in App store of everybody's server. - -The basic structure of a webapp is -- directory named `public` that contains the actual webapp: html, JavaScript and resources such as images and css files. This directory is automatically mounted by the server so that the webapp is available via http once installed -- package.json with special keywords that classifies the webapp - - `signalk-webapp` - standalone webapp - - `signalk-embeddable-webapp` - embeddable webapp - - `signalk-plugin-configurator` - plugin configuration form - -This structure is all that is needed for a standalone webapp. - -You can also include the following section in `package.json` to determine how your webapp appears in the _Webapps_ list: -```JSON - "signalk": { - "appIcon": "./assets/icons/icon-72x72.png", - "displayName": "Freeboard-SK" - }, -``` - -where: -- `appIcon` is the path (relative to the `public` directory) to an image within the package to display in the webapp list. The image should be at least 72x72 pixels in size. -- `displayName` is the name to appear in the webapp list. By default the `name` in the package.json is used, when supplied this text will be used instead. - -The embedded components are implemented using [Webpack Federated Modules](https://webpack.js.org/concepts/module-federation/) and [React Code Splitting](https://reactjs.org/docs/code-splitting.html). - -There is no keyword for a module that provides only embedded components, use `signalk-webapp` instead. - -You need to configured Webpack to create the necessary code for federation using *ModuleFederationPlugin* and expose the component with fixed names: -- embeddable webapp: `./AppPanel` -- plugin configuration form: `./PluginConfigurationPanel` -- embedded component: `./AddonPanel` - -The ModuleFederationPlugin library name must match the package name and be a "safe" name for a webpack module like in `library: { type: 'var', name: packageJson.name.replace(/[-@/]/g, '_') },` - -The exposed modules need to `export default` a React component - both class based components and stateless functional components can be used. The server dependencies like `reactstrap` can and should be used. Add `@signalk/server-admin-ui-dependencies` as a dependency to the webapp, it defines the depedencies used by the server admin UI. - -See the vesselpositions embedded webapp/component and Calibration plugin for examples of each. It is probably easier to start with either one and modify them to suit your needs. Don't forget to change the module id and name in package.json! - - -## Webapp / Component and Admin UI / Server interfaces - -Standalone Webapps can use the server's APIs (Standard Signal K http and WebSocket APIS as well as the server's endpoints) but they need to implement everything themselves. - -Embedded Webapps, Components and Plugin Configuration Forms work inside the Admin UI and they can interact with the Admin UI and the server with APIs exposed by the Admin UI as component properties. - -This documentation is rudimentary on purpose, as the details need to be worked out. - -Embedded webapp properties -- access to the login status of the browser user -- ability to render Login form instead of the webapp content -- getting and setting application data -- opening an automatically reconnecting WebSocket connection to the server -- getting Signal K data via `get` -- [Embedded](packages/server-admin-ui/src/views/Webapps/Embedded.js) - -PluginConfigurationForm properties -- `configuration` : the configuration data of the plugin -- `save`: function to save the configuration data -- [EmbeddedPluginConfigurationForm](packages/server-admin-ui/src/views/Configuration/EmbeddedPluginConfigurationForm.js) - diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 000000000..57c15ab1a --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,13 @@ +[book] +title="Signal K Server Documentation" +description = "A Guide for users and developers." +authors = [] +language = "en" +multilingual = false +src = "src" + +[build] +build-dir = "built" +create-missing = false + + diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 000000000..7f0059c92 --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,41 @@ +# Signal K Server + +[Introduction](./index.md) + +# Getting Started +* [Installation](./installation/install.md) + * [Installing on Raspberry Pi](./installation/raspberry_pi_installation.md) + * [Updating your installation](./installation/updating.md) + * [Runtime environment & options](./installation/command_line.md) +* [Security](./security.md) + * [Generating tokens](./setup/generating_tokens.md) +# Setup +* [Configuration](./setup/configuration.md) + * [Seatalk Connections](./setup/seatalk/seatalk.md) +# Feature How Tos + * [Anchor Alarm](./features/anchoralarm/anchoralarm.md) + * [NMEA0183 Server](./features/navdataserver/navdataserver.md) +# Support +* [Help & Support](./support/help.md) +* [Sponsor](./support/sponsor.md) +# Develop +* [Notes for Developers](./develop/developer_notes.md) +* [What's New in V2](./whats_new.md) + * [Changes & Deprecations](./breaking_changes.md) +* [WebApps](./develop/webapps.md) +* [Plugins](./develop/plugins/server_plugin.md) + * [Processing Data](./develop/plugins/deltas.md) + * [Server API](./develop/plugins/server_plugin_api.md) + * [Resource Providers](./develop/plugins/resource_provider_plugins.md) + * [Course Providers](./develop/rest-api/course_calculations.md) + * [Autopilot Providers](./develop/plugins/autopilot_provider_plugins.md) +* [Publishing to the AppStore](./develop/plugins/publishing.md) +* [REST APIs](./develop/rest-api/open_api.md) + * [Course API](./develop/rest-api/course_api.md) + * [Course Calculations](./develop/rest-api/course_calculations.md) + * [Resources API](./develop/rest-api/resources_api.md) + * [Notifications API](./develop/rest-api/notifications_api.md) + * [Autopilot API](./develop/rest-api/autopilot_api.md) + * [Anchor API](./develop/rest-api/anchor_api.md) +* [Contribute](./develop/contributing.md) + diff --git a/docs/src/breaking_changes.md b/docs/src/breaking_changes.md new file mode 100644 index 000000000..cf8a84ccd --- /dev/null +++ b/docs/src/breaking_changes.md @@ -0,0 +1,57 @@ +# Changes & Deprecations + +The introduction of new REST APIs in version 2 and the move to an operations based model has resulted in some version 1 paths being flagged for deprecation. + +It is recommended that applications and plugins referencing these deprecated paths update their operation to reference the version 2 paths. + +## Changes + +The following changes have been implemented with the introduction of **Resources API** and apply to applications using the `./signalk/v2/resources` endpoint. + +_Note: These changes DO NOT impact applications using the `./signalk/v1/resources` endpoint._ + +### 1. Resource ID prefix assignment + +The version 1 specification defined resource Ids with the following format `urn:mrn:signalk:uuid:`. + +_e.g. `urn:mrn:signalk:uuid:18592f80-3425-43c2-937b-0be64b6be68c`_ + +The Resource API has dropped the use the prefix and ids are now just a uuidv4 value. + +_e.g. `18592f80-3425-43c2-937b-0be64b6be68c`_ + +This format is used for both accessing a resource _e.g. `/signalk/v1/api/resources/waypoints/18592f80-3425-43c2-937b-0be64b6be68c`_ as well as the value within an `href` attribute. + +_Example:_ +``` +{ + "name": "...", + "descripton": "...", + "href": "/resources/waypoints/18592f80-3425-43c2-937b-0be64b6be68c", + ... +} +``` + +### 2. Resource Attributes + +The Resources API has updated the definition of the following resources and may break applications that simply shift to using the `v2` api without catering for the changes: +- **routes**: removed the `start`, `end` properties. +- **waypoints**: removed `position` attribute, added `name`, `description` and `type` attributes. +- **regions**: removed `geohash` attribute, added `name` and `description` properties. +- **notes**: removed `geohash` and `region` attributes, added `href` and `properties` attributes. +- **charts**: There has been a significant changes to include support for WMS, WMTS and TileJSON sources. + +Please see the [Resources OpenAPI definition](https://github.com/SignalK/signalk-server/blob/master/src/api/resources/openApi.json) for details. + + +--- + +## Deprecations: + +### 1. courseGreatCircle, courseRhumbline paths + +With the introduction of the Course API the following paths should now be considered deprecated: +- `/signalk/v1/api/vessels/self/navigation/courseGreatCircle` +- `/signalk/v1/api/vessels/self/navigation/courseRhumbline` + +_Note: The Course API does currently maintain values in these paths for the purposes of backward compatibility, but applications and plugins referencing these paths should plan to move to using the equivalent paths under `/signalk/v2/api/vessels/self/navigation/course`._ diff --git a/docs/CONTRIBUTING.md b/docs/src/develop/contributing.md similarity index 77% rename from docs/CONTRIBUTING.md rename to docs/src/develop/contributing.md index 00d0b372f..46a7db444 100644 --- a/docs/CONTRIBUTING.md +++ b/docs/src/develop/contributing.md @@ -1,9 +1,19 @@ -**Working on your first Pull Request?** You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) +# Contributing + +Signal K server is an Open Source project and contributions are welcome. + +Contributions are made by creating Pull Requests in the [GitHub repository](https://github.com/SignalK/signalk-server). + +_**Working on your first Pull Request?**_ + +You can learn how from this *free* series [How to Contribute to an Open Source Project on GitHub](https://egghead.io/series/how-to-contribute-to-an-open-source-project-on-github) + +--- ### Submitting a Pull Request (PR) Before you submit your Pull Request (PR) consider the following guidelines: -1. [Fork](https://help.github.com/articles/fork-a-repo/) the repo. +1. [Fork](https://help.github.com/articles/fork-a-repo/) the repository. 1. Make your changes in a new git branch: ```shell @@ -11,8 +21,8 @@ Before you submit your Pull Request (PR) consider the following guidelines: ``` 1. Create your patch. -1. Commit your changes using a descriptive commit message that follows - [Angular commit message conventions](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits). We are not 100% strict about this, but it really helps when reviewing the PR and in making commit history readable. The TL;DR of it is below. +1. Commit your changes using a descriptive commit message that follows the + [conventions outlined here](https://github.com/angular/angular.js/blob/master/DEVELOPERS.md#commits). Whilst we are not 100% strict about this, it really helps when reviewing the PR and in making the commit history readable. The TL;DR of it is below. - The subject line should be in the format `: `, where `` should be one of: - feat (feature) - fix (bug fix) @@ -38,10 +48,10 @@ Before you submit your Pull Request (PR) consider the following guidelines: git push origin my-fix-branch ``` -1. In GitHub, send a pull request to `signalk-server:master`. +1. In GitHub, create a pull request. * Use the same guidelines as commit messages to write the PR title and description. The server's release notes are automatically generated from PR titles, so think about how you can make them informative and easy to understand. The description should tell how the change affects the server's behavior and motivation for doing the change. * If we suggest changes to your PR we expect you to: - * Once agreed implement the changes. + * Implement the agreed changes. * Rebase your branch and force push to your GitHub repository (this will update your Pull Request): ```shell diff --git a/docs/src/develop/developer_notes.md b/docs/src/develop/developer_notes.md new file mode 100644 index 000000000..9b074c5ab --- /dev/null +++ b/docs/src/develop/developer_notes.md @@ -0,0 +1,46 @@ +# Notes for Developers + +Signal K Server has an extensible architecture that enables developers to add functionality to support new protocols, devices, information sources, etc. + +The information in this section aims to provide guidance, not only on how to develop plugins and applications to extend capability, but also how to do so in alignment with the Signal K specification, protocol and server architecture. +By understanding the underlying architecture, the plugins and apps you create will ensure that the additional functionality and data will be discoverable and work in harmony with other solutions. + +## Looking Ahead + +Signal K Server v2 marks the start of an evolution from the Signal K v1 approach of defining the paths, their hierarchy and the full data model schema, towards an approach centered around modular REST APIs (HTTP endpoints defined as OpenApi specifications). + +These APIs enact operations (i.e. activate a route, advance to next point, etc) rather just expose a generic data model with some well known paths. +They are available under the path `/signalk/v2/api` so they can coexist with v1 APIs. There is a connection with the Signal K full data model but, unlike the v1 APIs it is not 1:1, it is abstracted behind the interface. + +The reason for adopting this approach is to address the fact that many paths within a Signal K hierarchy are related, a change in the value of one path will require that the value of other paths be updated to ensure that the data model is consistent. +At present this relies on the plugin / application knowing which paths in the hierarchy are related. Additionally there may be other plugins / applications also updating some of the same paths which can cause the data model to become invalid, which then erodes trust in the data which impacts its use in navigation. + +The v1 model for using PUT handlers is also very vague and causes confusion. The aim of defining APIs with clear contracts using industry standard OpenApi mechanism is to make APIs discoverable and their use and semantics explicit. + +The use of APIs to perform operations addresses these issues providing the following benefits: +1. A standardised interface for all applications / plugins to perform an operation +1. Provides clear ownership of the paths in the Signal K data model +1. Ensures values are being maintained in all of the related paths. +1. Increases trust in the data for use in all scenarios. + +### Stream Interface + +Currently, when v2 REST APIs emit deltas that contain v2 paths and structure, but they do not end up in the full model. This means that these paths and values are only available via API GET requests. + +## Offline Use + +When operating on a vessel you should not assume that a connection to Internet services is available. +Therefore, it is important that the WebApps and Plugins you create be _"self contained"_ and provide all the resources they require to operate _(i.e. fonts, stylesheets, images, etc)_. This also minimises data charges even if your module does use data over Internet. + +For WebApps and Plugins that do connect to Internet based services to provide data, they should be resilient to changes in the connection status to those services and where necessary display their status. + + +## Deprecations and Breaking Changes + +With the move towards REST APIs and the desire to improve the data model (and also fix some mistakes) it's inevitable that there will be deprecations and breaking changes. + +For example, when addressing duplicate Great Circle and Rhumbline course paths, the resultant changes will likley break compatibility with v1. + +For details about paths that are flagged for deprecation see [Changes & Deprecations](../breaking_changes.md). + + diff --git a/docs/src/develop/plugins/autopilot_provider_plugins.md b/docs/src/develop/plugins/autopilot_provider_plugins.md new file mode 100644 index 000000000..b8692ab4e --- /dev/null +++ b/docs/src/develop/plugins/autopilot_provider_plugins.md @@ -0,0 +1,12 @@ +# Autopilot Provider Plugins + +#### (Under Development) + +_Note: This API is currently under development and the information provided here is likely to change._ + + +The Signal K server [Autopilot API](../rest-api/autopilot_api.md) will provide a common set of operations for interacting with autopilot devices and (like the Resources API) will rely on a "provider plugin" to facilitate communication with the autopilot device. + +By de-coupling the operation requests from device communication provides the ability to support a wide variety of devices. + +[View the PR](https://github.com/SignalK/signalk-server/pull/1596) for more details. diff --git a/docs/src/develop/plugins/deltas.md b/docs/src/develop/plugins/deltas.md new file mode 100644 index 000000000..5b4a67730 --- /dev/null +++ b/docs/src/develop/plugins/deltas.md @@ -0,0 +1,164 @@ +# Processing data from the server + +A plugin will generally want to: +1. Subscribe to data published by the server _(i.e. received from a NMEA 2000 bus, etc)_ +1. Emit data. + +In both cases the plugin will use *deltas* which the server uses to signal changes in the Signal K full data model. Delta messages contain the new value associated with a path (not the amount of change from the previous value.)_ + +_See the [Signal K Delta Specification](http://signalk.org/specification/1.7.0/doc/data_model.html#delta-format) for details._ + +Using the server API, plugins can either: +1. Get the current value of a path in the full model or +1. Subscribe to a path and access a stream of _deltas_ that updates every time the value is updated. + +By specifying a context _e.g. 'vessels.self'_ you can limit the number of delta messages received to those of host vesseel. +To receive all deltas you can specify `*` as the context. + +You can also limit the deltas received by the path you supply. +If you supply a specific path _e.g. navigation.position_, only updates in the value will be received. +Since paths are hierarchical, paths can contain wildcards _e.g._navigation.*_ which will deliver deltas containing updates to all paths under `navigation`. + +The data received is formatted as per the following example: +```javascript + { + path: 'navigation.position', + value: { longitude: 24.7366117, latitude: 59.72493 }, + context: 'vessel.self', + source: { + label: 'n2k-sample-data', + type: 'NMEA2000', + pgn: 129039, + src: '43' + }, + $source: 'n2k-sample-data.43', + timestamp: '2014-08-15T19:00:02.392Z' + } +``` + + +## Reading the current path value + +The server API provides the following methods for retrieving values from the full data model. +- `getSelfPath(path)` returns the value of the supplied `path` in the `vessels.self` context. +```javascript +const value = app.getSelfPath('uuid'); +app.debug(value); // Should output something like urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60 +``` + +- `getPath(path)` returns the value of the path (including the context) starting from the _root_ of the full data model. +```javascript +const baseStations = app.getPath('shore.basestations'); +``` + +## Subscribing to Deltas + +A can subscribe to a stream of updates (deltas) by creating the subscription. + +Subcriptions are generally manged in the plugin `start()` and `stop()` methods to ensure the subscribtions are _unsubscribed_ prior to the plugin stopping to ensure all resources are freed. + +The following example illustrates the pattern using the `subscriptionmanager` API method. + +```javascript +let unsubscribes = []; + +plugin.start = (options, restartPlugin) => { + app.debug('Plugin started'); + let localSubscription = { + context: '*', // Get data for all contexts + subscribe: [{ + path: '*', // Get all paths + period: 5000 // Every 5000ms + }] + }; + + app.subscriptionmanager.subscribe( + localSubscription, + unsubscribes, + subscriptionError => { + app.error('Error:' + subscriptionError); + }, + delta => { + delta.updates.forEach(u => { + app.debug(u); + }); + } + ); +}; + +plugin.stop = () => { + unsubscribes.forEach(f => f()); + unsubscribes = []; +}; +``` + +In the `start()` method create a subscription definition `localSubscription` which is then passed to `app.subscriptionmanager.subscribe()` as the first argument, we also pass the `unsubscribes` array in the second argument. + +The third argument is a function that will be called when there's an error. + +The final argument is a function that will be called every time an update is received. + +In the `stop()` method each subcription in the `unsubscribes` array is _unsubscribed_ and the resources released. + +## Sending Deltas + +A SignalK plugin can not only read deltas, but can also send them. This is done using the `handleMessage()` API method and supplying: + +1. The plugin id +2. A formatted delta update message +3. The Signal K version ['v1' or 'v2'] _(if omitted the default is 'v1')_. See [REST APIs](../openapi/index.md) for details. + +_Example:_ +```javascript +app.handleMessage( + plugin.id, + { + updates: [{ + values: [{ + path: 'environment.outside.temperature', + value: -253 + }] + }] + }, + 'v1' + ); +``` + + +## Sending NMEA 2000 data from a plugin + +A SignalK plugin can not only emit deltas, but can also send data such as NMEA 2000 data. + +This is done using the `emit()` API and specifying the provider as well as the formatted data to send. + +_Example: Send NMEA using Actisense serial format:_ +``` javascript + app.emit( + 'nmea2000out', + '2017-04-15T14:57:58.468Z,0,262384,0,0,14,01,0e,00,88,b6,02,00,00,00,00,00,a2,08,00'); +``` + +_Example: Send NMEA using Canboat JSON format:_ +```javascript + app.emit('nmea2000JsonOut', { + pgn: 130306, + 'Wind Speed': speed, + 'Wind Angle': angle < 0 ? angle + Math.PI*2 : angle, + 'Reference': "Apparent" + }); +``` + +### Sending a message on NMEA2000 startup + +If you need to send an NMEA2000 message out at startup, _e.g get current state from a device_ you will need to wait until the provider is ready before sending your message. + +_Example: Send NMEA after the provider is ready:_ +```javascript + app.on('nmea2000OutAvailable', () => { + app.emit( + 'nmea2000out', + '2017-04-15T14:57:58.468Z,2,6,126720,%s,%s,4,a3,99,01,00'); + }); +``` + + diff --git a/docs/src/develop/plugins/publishing.md b/docs/src/develop/plugins/publishing.md new file mode 100644 index 000000000..dc83ce894 --- /dev/null +++ b/docs/src/develop/plugins/publishing.md @@ -0,0 +1,69 @@ +# Publishing to The AppStore + + +Plugins and WebApps are available in the AppStore when they have been published to [npm repository](https://www.npmjs.com/) with the one or more of the following keywords in the `package.json` file: +- `signalk-node-server-plugin` +- `signalk-webapp` + +Additionally you can have your plugin appear within one or more AppStore categories by also adding the following keyword(s): +- `signalk-category-chart-plotters` +- `signalk-category-nmea-2000` +- `signalk-category-nmea-0183` +- `signalk-category-instruments` +- `signalk-category-hardware` +- `signalk-category-ais` +- `signalk-category-notifications` +- `signalk-category-digital-switching` +- `signalk-category-utility` +- `signalk-category-cloud` +- `signalk-category-weather` +- `signalk-category-deprecated` +- `signalk-category-hidden` (won't show on the App Store) + +To have your plugin start automatically after being installed, without requiring any configuration via the **Plugin Config** screen add the following key to the `package.json` file: + +```JSON +"signalk-plugin-enabled-by-default": true +``` + +To control the way your WebApp is displayed in the Admin UI add a `signalk` key with the following attributes: +```JSON + "signalk": { + "appIcon": "./img/icon-72x72.png", // path to an image file to use as an icon. + "displayName": "My SK App" // name to display in place of the package name. + } +``` + + +_Example: package.json_ +```JSON +{ + "name": "my-signalk-plugin-app", + "version": "1.0.0", + "description": "My great signalk plugin-app", + "keywords": [ + "signalk-node-server-plugin", + "signalk-webapp", + "signalk-category-ais" + ], + "signalk-plugin-enabled-by-default": true, + "signalk": { + "appIcon": "./assets/icons/icon-72x72.png", + "displayName": "My Great WebApp" + }, + "main": "plugin/index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "ISC" +} +``` + +#### Publishing your Plugin + +Once you have developed and tested your Plugin / WebApp you can publish it to make it visible in the AppStore. +To do this, in a terminal session from within the folder containing `package.json`: +```bash +npm publish +``` diff --git a/RESOURCE_PROVIDER_PLUGINS.md b/docs/src/develop/plugins/resource_provider_plugins.md similarity index 71% rename from RESOURCE_PROVIDER_PLUGINS.md rename to docs/src/develop/plugins/resource_provider_plugins.md index 43a31a104..9e899f85a 100644 --- a/RESOURCE_PROVIDER_PLUGINS.md +++ b/docs/src/develop/plugins/resource_provider_plugins.md @@ -1,62 +1,47 @@ # Resource Provider plugins -_This document should be read in conjunction with [SERVERPLUGINS.md](./SERVERPLUGINS.md) as it contains additional information regarding the development of plugins that facilitate the storage and retrieval of resource data._ - -To see an example of a resource provider plugin see [resources-provider-plugin](https://github.com/SignalK/signalk-server/tree/master/packages/resources-provider-plugin) - ---- ## Overview -The SignalK specification defines the path `/signalk/v2/api/resources` for accessing resources to aid in navigation and operation of the vessel. +The Signal K server _Resource API_ provides a common set operations for clients to interact with routes, waypoints, charts, etc but it does NOT provide the ability to persist or retrieve resources to / from storage. -It also defines the schema for the following __Common__ resource types: -- routes -- waypoints -- notes -- regions -- charts +This functionality needs to be provided by one or more server plugins that interface with the _Resource API_ to facilitate the storage and retrieval of resource data. -each with its own path under the root `resources` path _(e.g. `/signalk/v2/api/resources/routes`)_. +These plugins are called __Provider Plugins__. -It should also be noted that the `/signalk/v2/api/resources` path can also host other types of resource data which can be grouped within a __Custom__ path name _(e.g. `/signalk/v2/api/resources/fishingZones`)_. +_Resource API architecture:_ + -The SignalK server does not natively provide the ability to store or retrieve resource data for either __Common__ and __Custom__ resource types. -This functionality needs to be provided by one or more server plugins that handle the data for specific resource types. -These plugins are called __Resource Providers__. +This de-coupling of request handling and data storage provides the flexibility to persist resource data in a variety of different storage types as well as Internet based services. -The de-coupling of request handling and data storage provides flexibility to persist resource data in different types of storage to meet the needs of your SignalK implementation. -Requests for both __Common__ and __Custom__ resource types are handled by the SignalK server, the only difference being that the resource data contained in `POST` and `PUT` requests for __Common__ resource types is validated against the OpenApi schema. +_Note: Signal K server comes with the [resources-provider-plugin](https://github.com/SignalK/signalk-server/tree/master/packages/resources-provider-plugin) pre-installed which persists resource data to the local file system._ -_Note: A plugin can act as a provider for both __Common__ and __Custom__ resource types._ ---- -## Server Operation: +### Resources API: -The Signal K server handles all requests to `/signalk/v2/api/resources` (and sub-paths), before passing on the request to the registered resource provider plugin. +The _Resources API_ handles all client requests received via the `/signalk/v2/api/resources` path, before passing on the request to registered provider plugin(s). -The following operations are performed by the server when a request is received: -- Checks for a registered provider for the resource type -- Checks that the required ResourceProvider methods are defined -- Performs access control check -- For __Common__ resource types, checks the validity of the `resource id` and submitted `resource data`. +The _Resources API_ performs the following operations when a request is received: +1. Checks for registered provider(s) for the resource type _(i.e. route, waypoint, etc.)_ +1. Checks that the required ResourceProvider methods are defined for the requested operation _(i.e. POST, PUT, GET, DELETE)_ +1. Performs an access control check +1. `POST` and `PUT` requests for __Standard__ _(Signal K defined)_ resource types are checked for validity of the submitted: + - `resource id` + - `resource data` against the OpenAPI definition. -Only after successful completion of all these operations is the request passed on to the registered resource provider plugin. +Only after successful completion of all these operations is the request passed on to the registered provider plugin(s). --- -## Resource Provider plugin: -For a plugin to be considered a Resource Provider it needs to register with the SignalK server the following: -- Each resource type provided for by the plugin -- The methods used to action requests. It is these methods that perform the writing, retrieval and deletion of resources from storage. +## Provider plugins: +A resource provider plugin is a Signal K server plugin that implements the **Resource Provider Interface** which: +- Tells server the resource type(s) provided for by the plugin _(i.e. route, waypoint, etc.)_ +- Registers the methods used to action requests passed from the server and perform the writing, retrieval and deletion of resources from storage. -### Resource Provider Interface - ---- -The `ResourceProvider` interface is the means by which the plugin informs the SignalK server each of the resource type(s) it services and the endpoints to which requests should be passed. +Note: multiple providers can be registered for a resource type _(e.g. 2 x chart providers)_ The `ResourceProvider` interface is defined as follows in _`@signalk/server-api`_: @@ -68,7 +53,7 @@ interface ResourceProvider { ``` where: -- `type`: The resource type provided for by the plugin. These can be either __Common__ or __Custom__ resource types _(e.g. `'routes'`, `'fishingZones'`)_ +- `type`: The resource type provided for by the plugin. These can be either __Standard__ _(Signal K defined)_ or __Custom__ _(user defined)_ resource types _(e.g. `'routes'`, `'fishingZones'`)_ - `methods`: An object implementing the `ResourceProviderMethods` interface defining the functions to which resource requests are passed by the SignalK server. _Note: The plugin __MUST__ implement each method, even if that operation is NOT supported by the plugin!_ @@ -87,14 +72,12 @@ interface ResourceProviderMethods { ``` -### Methods and Resource Provider Implementation: +_**Note: The Resource Provider is responsible for implementing the methods and returning data in the required format!**_ ---- -**The Resource Provider is responsible for implementing the methods and returning data in the required format!** ---- +### Provider Methods: -__`listResources(query)`__: This method is called when a request is made for resource entries that match a specific criteria. +**`listResources(query)`**: This method is called when a request is made for resource entries that match a specific criteria. _Note: It is the responsibility of the resource provider plugin to filter the resources returned as per the supplied query parameters._ @@ -153,7 +136,8 @@ _Returns:_ ``` --- -__`getResource(id, property?)`__: This method is called when a request is made for a specific resource entry with the supplied `id`. If `property` is supplied then the value of the resource property is returned. If there is no resource associated with the id the call should return Promise.reject. + +**`getResource(id, property?)`**: This method is called when a request is made for a specific resource entry with the supplied `id`. If `property` is supplied then the value of the resource property is returned. If there is no resource associated with the id the call should return Promise.reject. - `id`: String containing the target resource entry id. _(e.g. '07894aba-f151-4099-aa4f-5e5773734b99')_ - `property` (optional): Name of resource property for which to return the value (in dot notation). _e.g. feature.geometry.coordinates_ @@ -212,7 +196,8 @@ _Returns:_ ``` --- -__`setResource(id, value)`__: This method is called when a request is made to save / update a resource entry with the supplied id. The supplied data is a complete resource record. + +**`setResource(id, value)`**: This method is called when a request is made to save / update a resource entry with the supplied id. The supplied data is a complete resource record. - `id:` String containing the id of the resource entry created / updated. _e.g. '07894aba-f151-4099-aa4f-5e5773734b99'_ @@ -269,7 +254,8 @@ setResource( ``` --- -__`deleteResource(id)`__: This method is called when a request is made to remove the specific resource entry with the supplied resource id. + +**`deleteResource(id)`**: This method is called when a request is made to remove the specific resource entry with the supplied resource id. - `id:` String containing the target resource entry id. _e.g. '07894aba-f151-4099-aa4f-5e5773734b99'_ @@ -287,9 +273,10 @@ deleteResource( ); ``` -### Registering a Resource Provider: --- +### Registering as a Resource Provider: + To register a plugin as a provider for one or more resource types with the SignalK server, it must call the server's `registerResourceProvider` function for each resource type being serviced during plugin startup. The function has the following signature: @@ -302,7 +289,7 @@ where: _Note: More than one plugin can be registered as a provider for a resource type._ -_Example:_ +_Example: Plugin registering as a routes & waypoints provider._ ```javascript import { ResourceProvider } from '@signalk/server-api' @@ -374,14 +361,11 @@ module.exports = function (app) { ### Methods -A Resource Provider plugin must implement methods to service the requests passed from the server. - -All methods must be implemented even if the plugin does not provide for a specific request. - -Each method should return a __Promise__ on success and `throw` on error or if a request is not serviced. - +A Resource Provider plugin must implement ALL methods to service the requests passed from the server. +Each method should return a __Promise__ on success and `throw` on error, if a request is not serviced or is not implemented. +_Example:_ ```javascript // SignalK server plugin module.exports = function (app) { diff --git a/docs/src/develop/plugins/server_plugin.md b/docs/src/develop/plugins/server_plugin.md new file mode 100644 index 000000000..af8ef0f0e --- /dev/null +++ b/docs/src/develop/plugins/server_plugin.md @@ -0,0 +1,380 @@ +# Server plugins + + +## Overview + +Signal K Node server plugins are components that extend functionality of the server. +They are installed via the AppStore and configured via the Admin UI. + +Signal K server exposes an interface for plugins to use in order to interact with the full data model, emit delta messages and process requests. + +Plugins can also provide a webapp by placing the relavent files in a folder named `/public/` which the server will mount under `http://{skserver}:3000/{pluginId}`. + +**Note: With the move towards Signal K server providing APIs to perform operations, it is important that you consider how the proposed functionality provided by your plugin aligns with the Signal K architecture before starting development.** + +For example, if the plugin you are looking to develop is providing access to information such as `route,` `waypoint`, `POI`, or `charts` you should be creating a _[Resources Provider Plugin](./resource_provider_plugins.md)_ for the _[Resources API](../openapi/resources_api.md)_. + +Or if you are looking to perform course calculations or integrate with an auotpilot, you will want to review the _[Course API](../openapi/course_api.md)_ documentation prior to commencing your project. + + +**OpenApi description for your plugin's API** + +If your plugin provides an API you should consider providing an OpenApi description. This promotes cooperation with other plugin/webapp authors and also paves the way for incorporating new APIs piloted within a plugin into the Signal K specification. _See [Add OpenAPI definition](#add-an-openapi-definition)_ below. + +--- + +## Getting Started with Plugin Development + +### Prerequisites + +To get started developing your plugin you will need the following: +- Signal K server instance on your device _(clone of GIT repository or docker instance)_ +- NodeJs version 18 or later and NPM installed +- SignalK server configuration folder. _(Created when Signal K server is started. default location is `$HOME/.signalk`)_. + +--- + +### Setting up your project + +1. Create a folder for your plugin code and create the necessary file structure: +```bash +mkdir my-pluin +cd my-plugin +npm init # create package.json file +``` + +2. Create the folders to hold your plugin code and webapp UI. +```bash +/my-plugin + /plugin # plugin (javascript code / built typesrcipt code) + index.js + .. + /public # web app UI + index.html + .. + /src # typescript source code (not required if using javascript) + index.ts + ... + package.json +``` + +3. Update the `package.json` to reflect your project structure and add keywords to identify the package for the Signal K AppStore. + +```JSON +{ + "name": "my-plugin", + "version": "1.0.0", + "description": "My signalk plugin", + "keywords": [ + "signalk-node-server-plugin", + "signalk-category-ais" + ], + "signalk-plugin-enabled-by-default": false, + "signalk": { + "appIcon": "./assets/icons/icon-72x72.png", + "displayName": "My Great WebApp" + }, + "main": "plugin/index.js", + ... +} +``` + +4. _Optional:_ Install any dependencies or third party packages. +```bash +npm i +``` + + +### Link your project to Signal K server. + +Once you have developed your plugin code and are ready to debug, the most convenient way is to use `npm link` to link your plugin code to your instance of Signal K server. + +To do this, from within a terminal window: +```bash +# Ensure you are in the folder containing your built plugin code +cd my_plugin_src + +# Create a link (may require the use of sudo) +npm link + +# Change to the Signal K server configuration directory +cd ~/.signalk + +# Link your plugin using the name in the package.json file +#(may require the use of sudo) +npm link my-signalk-plugin-app +``` + +When you start Signal K server the plugin will now appear in the **Plugin Config** screen where it can be configured and enabled. + + +### Debugging + +The simplest way to debug your plugin is to turn on **Enable Debug log** for your plugin in the **Plugin Config** screen. + +Alternatively, you can debug your plugin by starting the Signal K server with the `DEBUG` environment variable: +```bash +$ DEBUG=my-signalk-plugin signalk-server + +# sample output +my-signalk-plugin Plugin stopped +0ms +my-signalk-plugin Plugin started +2ms +``` + +You can also view debug information about the plugin loading process: +```bash +$ DEBUG=signalk:interfaces:plugins signalk-server + +# sample output +signalk:interfaces:plugins Registering plugin my-signalk-plugin +0ms +signalk:interfaces:plugins Could not find options for plugin my-signalk-plugin, returning empty options: +2ms +``` + +#### Sample Data + +For development purposes, it's often nice to have some mocked data. SignalK comes with a synthesized NMEA2000 data set that can be used as sample data. + +You can enable this by adding `--sample-n2k-data` to the command line: +```bash +$ DEBUG=my-signalk-plugin signalk-server --sample-n2k-data +``` +--- + +## Start Coding + +Signal K server plugins are NodeJs `javascript` or `typescript` projects that return an object that implements the `Plugin` interface. + +They are installed into the `node_modules` folder that resides inside the SignalK server's configuration directory _(`$HOME/.signalk` by default)_. + +A Signal K plugin is passed a reference to the Signal K server plugin interface which it can use to interact with the server. + +Following are code snippets that can be used as a template for plugin development ensuring the returned Plugin object contains the required functions. + +### Javascript + +Create `index.js` with the following content: + +```javascript +module.exports = (app) => { + + const plugin = { + id: 'my-signalk-plugin', + name: 'My Great Plugin', + start: (settings, restartPlugin) => { + // start up code goes here. + }, + stop: () => { + // shutdown code goes here. + }, + schema: () => { + properties: { + // plugin configuration goes here + } + } + }; + + return plugin; +}; +``` + +### Typescript + +Create `index.js` with the following content: + +```typescript +import { Plugin, PluginServerApp } from '@signalk/server-api'; + +module.exports = (app: PluginServerApp): Plugin => { + + const plugin: Plugin = { + id: 'my-signalk-plugin', + name: 'My Great Plugin', + start: (settings, restartPlugin) => { + // start up code goes here. + }, + stop: () => { + // shutdown code goes here. + }, + schema: () => { + properties: { + // plugin configuration goes here + } + } + }; + + return plugin; +} +``` + +A plugin must return an object containing the following functions: + +- `start(settings, restartPlugin)`: This function is called when the plugin is enabled or when the server starts (and the plugin is enabled). The `settings` parameter contains the configuration data entered via the **Plugin Config** screen. `restartPlugin` is a function that can be called by the plugin to restart itself. + +- `stop()`: This function is called when the plugin is disabled or after configuration changes. Use this function to "clean up" the resources consumed by the plugin i.e. unsubscribe from streams, stop timers / loops and close devices. + +- `schema()`: A function that returns an object defining the schema of the plugin's configuration data. It is used by the server to generate the user interface in the **Plugin Config** screen. + +_Note: When a plugin's configuration is changed the server will first call `stop()` to stop the plugin and then `start()` with the new configuration data._ + +A plugin can also contain the following optional functions: + +- `uiSchema()`: A function that returns an object defining the attributes of the UI components displayed in the **Plugin Config** screen. + +- `registerWithRouter(router)`: This function (which is called during plugin startup) enables plugins to provide an API by registering paths with the Express router is passed as a parameter when invoked. It is strongly recommended that he plugin implement `getOpenAPI()` if this function is used. + +_Example:_ +```javascript +plugin.registerWithRouter = (router) => { + router.get('/preferences', (req, res) => { + res.status(200).json({ + preferences: { + color: 'red', + speed: 1.23 + } + }); + }); +}; + +``` + +- `getOpenApi()`: Function to return the OpenAPI definition. This should be implemented when your plugin provides HTTP endpoints for clients to call. Doing so makes the OpenAPI definition available in the server Admin UI under `Documentation -> OpenAPI`. + +_Example:_ +```javascript +const openapi = require('./openApi.json'); + +plugin.getOpenApi = () => openapi; + +``` + +--- + +## Plugin configuration / Schema + +A plugin's `schema` function must return a [JSON Schema](http://json-schema.org/) object decribing the structure of the configuration data. This is used by the server to render the plugin's configuration screen in the Admin UI. + +The configuration data is stored by the server under the following path `$SIGNALK_NODE_CONFIG_DIR/plugin-config-data/.json`. _(Default value of SIGNALK_NODE_CONFIG_DIR is $HOME/.signalk.)_ + +_Example:_ +```javascript + plugin.schema = { + type: 'object', + required: ['some_string', 'some_other_number'], + properties: { + some_string: { + type: 'string', + title: 'Some string that the plugin needs' + }, + some_number: { + type: 'number', + title: 'Some number that the plugin needs', + default: 60 + }, + some_other_number: { + type: 'number', + title: 'Some other number that the plugin needs', + default: 5 + } + } + }; +``` +JSON Schema approach works reasonably well for simple to medium complex configuration data. + +It should ne noted that some JSON schema constructs are not supported. Refer ([details](https://github.com/peterkelly/react-jsonschema-form-bs4/blob/v1.7.1-bs4/docs/index.md#json-schema-supporting-status)) for details. + +The server supports also [custom plugin configuration components](https://github.com/SignalK/signalk-server/blob/master/WEBAPPS.md), bypassing the automatic configuration format generation. + +The plugin is passed the configuration settings as the first parameter of the `start` function. +```javascript +plugin.start = (settings, restartPlugin) => { + // settings contains the plugin configuration + ... +} +``` + +### UI Schema +The plugin can define `uiSchema` by returning a [uiSchema object](https://github.com/mozilla-services/react-jsonschema-form#the-uischema-object) which is used to control how the user interface is rendered in the Admin UI. + +_Example: Make all data in an object called 'myObject' collapsible: +```javascript +uiSchema['myObject'] = { + 'ui:field': 'collapsible', + collapse: { + field: 'ObjectField', + wrapClassName: 'panel-group' + } +} +``` + +For more information, see [react-jsonschema-form-extras](https://github.com/RxNT/react-jsonschema-form-extras#collapsible-fields-collapsible) + + +### Making a plugin enabled by default +If your plugin does not require any initial configuration, you can enable it to start when the Signal K server is restarted after the plugin is installed. + +To do this add the following to the `package.json`: + +```json + "signalk-plugin-enabled-by-default": true +``` +--- + +## Add an OpenAPI Definition + +If your plugin exposes an API to interact with it then you should include an OpenAPI definition. + +You do this by creating an OpenAPI definition within the file `openApi.json` and then returning the content of the file with the `getOpenApi` method. + +_Example:_ +```javascript +const openapi = require('./openApi.json'); +... + +plugin.getOpenApi = () => openapi; +``` + +This will include your plugin's OpenApi definition in the documentation in the server's Admin UI under _Documentation -> OpenAPI_. + +Note: If the plugin's OpenApi description DOES NOT include a `servers` property, the API path presented in the documentation will be relative to the Signal K API path. You should include this property the plugin API is rooted elsewhere. +_Example:_ +```JSON + "servers": [ + { + "url": "/myapi/endpoint" + } + ], +``` + +See [testplugin](https://github.com/SignalK/signalk-server/tree/b82477e63ebdc14878164ce1ed3aedd80c5a8b0c/test/plugin-test-config/node_modules/testplugin) for an example. + +--- + +## Logging + +To record deltas sent by the plugin in the server's data log, enable the **Log plugin output** in the plugin configuration screen. + +--- + +## Removing a plugin + +Plugins can be removed via the AppStore. + +You can also remove a plugin manually by: +1. Deleting it's folder under `~/.signalk/node_modules` +1. Deleting it's entry from `~/.signalk/package.json` +1. Run `npm prune` from the `~/.signalk/` directory. + +Alternatively you can: +1. Remove the folder `~/.signalk/node_modules` +1. Run `npm install` from the `~/.signalk/` directory. + + +Finally you can remove the plugin setting file in `~/.signalk/plugin-config-data/`. + +--- + +## Examples + +Following are links to some published SignalK plugins that serve as an example of working plugins: +- [set-system-time](https://github.com/SignalK/set-system-time/blob/master/index.js) +- [Ais Reporter](https://github.com/SignalK/aisreporter/issues) diff --git a/docs/src/develop/plugins/server_plugin_api.md b/docs/src/develop/plugins/server_plugin_api.md new file mode 100644 index 000000000..7dc8f7ab9 --- /dev/null +++ b/docs/src/develop/plugins/server_plugin_api.md @@ -0,0 +1,732 @@ + + +# Server API for plugins + +SignalK server provides an interface to allow plugins to access / update the full data model, operations and send / receive deltas (updates). + +These functions are available via the `app` passed to the plugin when it is invoked. + +--- + +### Accessing the Data Model + +#### `app.getPath(path)` + +Returns the entry for the provided path starting from the `root` of the full data model. + +_Example:_ +```javascript +let baseStations = app.getPath('shore.basestations'); + +// baseStations: +{ + 'urn:mrn:imo:mmsi:2766140': { + url: 'basestations', + navigation: { position: {latitude: 45.2, longitude: 76.4} }, + mmsi: '2766140' + }, + 'urn:mrn:imo:mmsi:2766160': { + url: 'basestations', + navigation: { position: {latitude: 46.9, longitude: 72.22} }, + mmsi: '2766160' + } +} +``` + +#### `app.getSelfPath(path)` + +Returns the entry for the provided path starting from `vessels.self` in the full data model. + +_Example:_ +```javascript +let uuid = app.getSelfPath('uuid'); +// Note: This is synonymous with app.getPath('vessels.self.uuid') + +app.debug(uuid); +// urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60 +``` + +#### `app.registerPutHandler(context, path, callback, source)` + +Register a handler to action [`PUT`](http://signalk.org/specification/1.3.0/doc/put.html) requests for a specific path. + +The action handler can handle the request synchronously or asynchronously. + +The `callback` parameter should be a function which accepts the following arguments: +- `context` +- `path` +- `value` +- `callback` + +For synchronous actions, the handler must return a value describing the response of the request: + +```javascript +{ + state: 'COMPLETED', + statusCode: 200 +} +``` + + or + + ```javascript +{ + state:'COMPLETED', + statusCode: 400, + message:'Some Error Message' +} + ``` + + The `statusCode` value can be any valid HTTP response code. + +For asynchronous actions, that may take considerable time to complete and the requester should not be kept waiting for the result, the handler must return: + +```javascript +{ state: 'PENDING' } +``` + +When the action has completed the handler should call the `callback` function with the result: + +```javascript +callback({ state: 'COMPLETED', statusCode: 200 }) +``` +or + +```javascript +callback({ + state:'COMPLETED', + statusCode: 400, + message:'Some Error Message' +}) +``` + +_Example: Synchronous response:_ +```javascript +function myActionHandler(context, path, value, callback) { + if(doSomething(context, path, value)){ + return { state: 'COMPLETED', statusCode: 200 }; + } else { + return { state: 'COMPLETED', statusCode: 400 }; + } +} + +plugin.start = (options) => { + app.registerPutHandler('vessels.self', 'some.path', myActionHandler, 'somesource.1'); +} +``` + +_Example: Asynchronous response:_ +```javascript +function myActionHandler(context, path, value, callback) { + + doSomethingAsync(context, path, value, (result) =>{ + if(result) { + callback({ state: 'COMPLETED', result: 200 }) + } else { + callback({ state: 'COMPLETED', result: 400 }) + } + }); + + return { state: 'PENDING' }; +} + +plugin.start = (options) => { + app.registerPutHandler('vessels.self', 'some.path', myActionHandler); +} +``` +--- + +### Working with Deltas + +#### `app.handleMessage(pluginId, delta, skVersion = 'v1')` + +Emit a delta message. + +_Note: These deltas are handled by the server in the same way as any other incoming deltas._ + +_Example:_ +```javascript +app.handleMessage('my-signalk-plugin', { + updates: [ + { + values: [ + { + path: 'navigation.courseOverGroundTrue', + value: 1.0476934 + } + ] + } + ] +}); +``` + +Plugins emitting deltas that use Signal K v2 paths (like the [Course API](http://localhost:3000/admin/openapi/?urls.primaryName=course) paths) should call `handleMessage` with the optional `skVersion` parameter set to `v2`. This prevents v2 API data getting mixed in v1 paths' data in full data model & the v1 http API. + +Omitting the `skVersion` parameter will cause the delta to be sent as `v1`. + + +#### `app.streambundle.getBus(path)` + +Get a [Bacon JS](https://baconjs.github.io/) stream for a Signal K path that will stream values from any context. + +The `path` parameter is optional. If it is not provided the returned stream produces values for all paths. + +Stream values are objects with the following structure: +```javascript + { + path: ..., + value: ..., + context: ..., + source: ..., + $source: ..., + timestamp: ... + } +``` + +_Example:_ +```javascript +app.streambundle + .getBus('navigation.position') + .forEach(pos => app.debug(pos)); + +// output +{ + path: 'navigation.position', + value: { longitude: 24.7366117, latitude: 59.72493 }, + context: 'vessels.urn:mrn:imo:mmsi:2766160', + source: { + label: 'n2k-sample-data', + type: 'NMEA2000', + pgn: 129039, + src: '43' + }, + '$source': 'n2k-sample-data.43', + timestamp: '2014-08-15T19:00:02.392Z' +} +{ + path: 'navigation.position', + value: { longitude: 24.82365, latitude: 58.159598 }, + context: 'vessels.urn:mrn:imo:mmsi:2766140', + source: { + label: 'n2k-sample-data', + type: 'NMEA2000', + pgn: 129025, + src: '160' + }, + '$source': 'n2k-sample-data.160', + timestamp: '2014-08-15T19:00:02.544Z' +} +``` + +#### `app.streambundle.getSelfBus(path)` + +Get a [Bacon JS](https://baconjs.github.io/) stream for path from the `vessels.self` context. + +The `path` parameter is optional. If it is not provided the returned stream contains values for all paths. + +_Example:_ +```javascript +app.streambundle + .getSelfBus('navigation.position') + .forEach(pos => app.debug(pos)); + +// output +{ + path: 'navigation.position', + value: { longitude: 24.7366117, latitude: 59.72493 }, + context: 'vessels.urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60', + source: { + label: 'n2k-sample-data', + type: 'NMEA2000', + pgn: 129039, + src: '43' + }, + '$source': 'n2k-sample-data.43', + timestamp: '2014-08-15T19:00:02.392Z' +} +{ + path: 'navigation.position', + value: { longitude: 24.7366208, latitude: 59.7249198 }, + context: 'vessels.urn:mrn:signalk:uuid:a9d2c3b1-611b-4b00-8628-0b89d014ed60', + source: { + label: 'n2k-sample-data', + type: 'NMEA2000', + pgn: 129025, + src: '160' + }, + '$source': 'n2k-sample-data.160', + timestamp: '2014-08-15T19:00:02.544Z' +} +``` + +#### `app.streambundle.getSelfStream(path)` + +Get a [Bacon JS](https://baconjs.github.io/) stream for a path in the `vessels.self` context. + +The `path` argument is optional. If it is not provided the returned stream produces values for all paths. + +_Note: This is similar to `app.streambundle.getSelfBus(path)`, except that the stream values contain only the `value` property from the incoming deltas._ + +_Example:_ +```javascript +app.streambundle + .getSelfStream('navigation.position') + .forEach(pos => app.debug(pos)); + +// output + + my-signalk-plugin { longitude: 24.736677, latitude: 59.7250108 } +600ms + my-signalk-plugin { longitude: 24.736645, latitude: 59.7249883 } +321ms + my-signalk-plugin { longitude: 24.7366563, latitude: 59.7249807 } +174ms + my-signalk-plugin { longitude: 24.7366563, latitude: 59.724980699999996 } +503ms +``` + + +#### `app.streambundle.getAvailablePaths()` + +Get a list of available full data model paths maintained by the server. + +_Example:_ +```javascript +app.streambundle.getAvailablePaths(); + +// returns +[ + "navigation.speedOverGround", + "navigation.courseOverGroundTrue", + "navigation.courseGreatCircle.nextPoint.position", + "navigation.position", + "navigation.gnss.antennaAltitude", + "navigation.gnss.satellites", + "navigation.gnss.horizontalDilution", + "navigation.gnss.positionDilution", + "navigation.gnss.geoidalSeparation", + "navigation.gnss.type","navigation.gnss.methodQuality", + "navigation.gnss.integrity", + "navigation.magneticVariation", +] +``` + +#### `app.registerDeltaInputHandler ((delta, next) => {} )` + +Register a function to intercept all delta messages _before_ they are processed by the server. + +The callback function should call `next(delta)` with either: +- A modified delta (if it wants to alter the incoming delta) +- With the original delta to process it normally. + +_Note: Not calling `next(delta)` will cause the incoming delta to be dropped and will only show in delta statistics._ + +Other, non-delta messages produced by provider pipe elements are emitted normally. + +```javascript +app.registerDeltaInputHandler((delta, next) => { + delta.updates.forEach(update => { + update.values.forEach(pathValue => { + if(pathValue.startsWith("foo")) { + pathValue.path = "bar" + } + }) + }) + next(delta) +}); +``` + +--- + +### Configuration + +#### `app.savePluginOptions(options, callback)` + +Save changes to the plugin's configuration options. + +_Example:_ +```javascript +let options = { + myConfigValue = 'Something the plugin calculated' +}; + +app.savePluginOptions(options, () => {app.debug('Plugin options saved')}); +``` + +#### `app.readPluginOptions()` + +Read the stored plugin configuration options. + +_Example:_ +```javascript +let options = app.readPluginOptions(); +``` + +#### `app.getDataDirPath()` + +Returns the full path of the directory where the plugin can persist its internal data, e.g. data files, etc. + +_Example:_ +```javascript +let myDataFile = require('path').join( app.getDataDirPath(), 'somedatafile.ext') +``` +--- + +### Messages and Debugging + +#### `app.setPluginStatus(msg)` + +Set the current status of the plugin that is displayed in the plugin configuration UI and the Dashboard. + +The `msg` parameter should be a short text message describing the current status of the plugin. + +_Example:_ +```javascript +app.setPluginStatus('Initializing'); +// Do something +app.setPluginStatus('Done initializing'); +``` + +_Note: Replaces deprecated `setProviderStatus()`_ + +#### `app.setPluginError(msg)` + +Set the current error status of the plugin that is displayed in the plugin configuration UI and the Dashboard. + +The `msg` parameter should be a short text message describing the current status of the plugin. + +_Example:_ +```javascript +app.setPluginError('Error connecting to database'); +``` + +_Note: Replaces deprecated `setProviderError()`_ + + +#### `app.debug(...)` + +Log debug messages. + +This function exposes the `debug` method from the [debug module](https://www.npmjs.com/package/debug). +The npm module name is used as the debug name. + +`app.debug()` can take any type and will serialize it before outputting. + +_Note: Do not use `debug` from the debug module directly! Using `app.debug()`provided by the server ensures that the plugin taps into the server's debug logging system, including the helper switches in Admin UI's Server Log page. + +#### `app.error(message)` + +Report errors in a human-oriented message. Currently just logs the message, but in the future error messages hopefully will show up in the admin UI. + + +#### `reportOutputMessages(count)` + +Report to the server that the plugin has sent data to other hosts so it can be displayed on the Dashboard. + +_Note: This function is for use when the plugin is sending data to hosts other than the Signal K server (e.g. +network packets, http requests or messages sent to a broker)._ + +_**This function should NOT be used for deltas that the plugin sends with `handleMessage()`!**_ + +The `count` parameter is _optional_ and represents the number of messages sent between this call the previous +call. If omitted the call will count as one output message. + +_Example:_ +```javascript +app.reportOutputMessages(54); +``` + +--- + + +### Serial Port + +#### `app.getSerialPorts() => Promise` + +This returns a Promise which will resolve to a Ports object which contains information about the serial ports available on the machine. + +--- + +### Resources API Interface + +#### `app.registerResourceProvider(ResourceProvider)` + +Used by _Resource Provider plugins_ to register each resource type it handles. + +See [`Resource Provider Plugins`](../plugins/resource_provider_plugins.md#registering-as-a-resource-provider) for details. + + +#### `app.resourcesApi.setResource(resource_type, resource_id, resource_data, provider_id?)` + +Create / update value of the resource with the supplied SignalK resource_type and resource_id. + +_Note: Requires a registered Resource Provider for the supplied `resource_type`._ + + - `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ + or user defined resource types. + + - `resource_id`: The resource identifier. _(e.g. `ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a`)_ + + - `resource_data`: A complete and valid resource record. + + - `provider_id` (optional): The id of the Resource Provider plugin to use to complete the request. Most commonly used for creating a new resource entry when more than one provider is registered for the specified resource type. + +- returns: `Promise` + +_Example:_ +```javascript +app.resourcesApi.setResource( + 'waypoints', + 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a', + { + "position": {"longitude": 138.5, "latitude": -38.6}, + "feature": { + "type":"Feature", + "geometry": { + "type": "Point", + "coordinates": [138.5, -38.6] + }, + "properties":{} + } + } +).then ( () => { + // success + ... +}).catch (error) { + // handle error + console.log(error.message); + ... +} +``` + +#### `app.resourcesApi.deleteResource(resource_type, resource_id, provider_id?)` + +Delete the resource with the supplied SignalK resource_type and resource_id. + +_Note: Requires a registered Resource Provider for the supplied `resource_type`._ + +- `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ +or user defined resource types. + +- `resource_id`: The resource identifier. _(e.g. `ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a`)_ + +- `provider_id` (optional): The id of the Resource Provider plugin to use to complete the request. + +- returns: `Promise` + +_Example:_ +```javascript +app.resourcesApi.deleteResource( + 'notes', + 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a' +).then ( () => { + // success + ... +}).catch (error) { + // handle error + console.log(error.message); + ... +} +``` + +#### `app.resourcesApi.listResources(resource_type, params, provider_id?)` + +Retrieve collection of resource entries of the supplied resource_type matching the provided criteria. + +_Note: Requires a registered Resource Provider for the supplied `resource_type`._ + + - `resource_type`: Any Signal K _(i.e. `routes`,`waypoints`, `notes`, `regions` & `charts`)_ + or user defined resource types. + + - `params`: Object contining `key | value` pairs repesenting the crteria by which to filter the returned entries. + + - `provider_id` (optional): The id of the Resource Provider plugin to use to complete the request. + + __Note: The registered Resource Provider must support the supplied parameters for results to be filtered.__ + +- returns: `Promise<{[key: string]: any}>` + +_Example:_ +```javascript +app.resourcesApi.listResources( + 'waypoints', + {region: 'fishing_zone'} +).then (data => { + // success + console.log(data); + ... +}).catch (error) { + // handle error + console.log(error.message); + ... +} +``` +--- + +### Course API Interface + +The [`Course API`](../rest-api/course_api.md) provides the following functions for use by plugins. + + +#### `app.getCourse()` + +Retrieves the current course information. + +- returns: Resolved Promise on success containing the same course information returned by the [`/course`](/doc/openapi/?urls.primaryName=course#/course/get_course) API endpoint. + + +#### `app.clearDestination()` + +Cancels navigation to the current point or route being followed. + +- returns: Resolved Promise on success. + + +#### `app.setDestination(dest)` + +Set course to a specified position / waypoint. + +- `dest`: Object containing destination position information as per [`/course/destination`](/doc/openapi/?urls.primaryName=course#/destination/put_course_destination). + +- returns: Resolved Promise on success. + + +#### `app.activateRoute(rte)` + +Follow a route. +in the specified direction and starting at the specified point. + +- `rte`: Object containing route information as per [`/course/activeRoute`](/doc/openapi/?urls.primaryName=course#/activeRoute/put_course_activeRoute). + +- returns: Resolved Promise on success. + +--- + +### Notifications API _(proposed)_ + +#### `app.notify(path, value, pluginId)` + +Notifications API interface method for raising, updating and clearing notifications. + + - `path`: Signal K path of the notification + + - `value`: A valid `Notification` object or `null` if clearing a notification. + + - `pluginId` The plugin identifier. + + +To raise or update a for a specified path, call the method with a valid `Notification` object as the `value`. + +- returns: `string` value containing the `id` of the new / updated notification. + +_Example:_ +```javascript +const alarmId = app.notify( + 'myalarm', + { + message: 'My cutom alarm text', + state: 'alert' + }, + 'myAlarmPlugin' +) + +// alarmId = "ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a" +``` + +To clear (cancel) a notification call the method with `null` as the `value`. + +- returns: `void`. + +_Example: Clear notification_ +```javascript +const alarmId = app.notify( + 'myalarm', + null, + 'myAlarmPlugin' +) +``` + +--- + +### PropertyValues + +The _PropertyValues_ mechanism provides a means for passing configuration type values between different components running in the server process such as plugins and input connections. + +A plugin can both *emit* values and *listen* for values emitted by others. + +The difference between the _PropertyValues_ mechanism and _Event Emitters_ in NodeJs is that when `onPropertyValues` is called, the `callback()` function will be invoked and passed an array containing all of the previous values for that _property name_, starting with the initial value of `undefined`. If no values have been emitted for that _property name_ the callback will be invoked with a value of `undefined`. + +```typescript +app.emitPropertyValue: (name: string, value: any) => void + +onPropertyValues: ( + name: string, + callback: (propValuesHistory: PropertyValue[]) => void +) => Unsubscribe +``` + +**PropertyValue** has the following structure: +```typescript +interface PropertyValue { + timestamp: number // millis + setter: string // plugin id, server, provider id + name: string + value: any +} +``` + +_Note that the value can be also a function._ + +This mechanism allows plugins to _offer_ extensions via _"Well Known Properties"_, for example +- additional [NMEA0183 sentence parsers for custom sentences](https://github.com/SignalK/nmea0183-signalk/pull/193) via `nmea0183sentenceParser` +- additional PGN definitions for propietary or custom PGNs + +Code handling incoming _PropertyValues_ should be fully reactive due to: +- Plugins being able to emit _PropertyValues_ when they activated and / or started +- There being no defined load / startup order for plugins / connections. + +So even if all plugins / connections emit during their startup, you cannot depend on a specific _PropertyValue_ being available. It may be present when your code starts or it may arrive after your code has started. + + +**Note: The _PropertyValues_ mechanism is not intended to be used for data passing on a regular basis, as the total history makes it a potential memory leak.** + +To safeguard against a component accidentally emitting regularly, via a fixed upper bound is enforced for the value array per _property name_. New values will be ignored if the upper bound is reached and are logged as errors. + +--- + +### Exposing custom HTTP paths & OpenApi + +Plugins are able to provide an API via a function called `registerWithRouter(router)`, which like the plugin's `start` and `stop` functions, will be called during plugin startup with an _Express_ router as the parameter. + +The router will be mounted at `/plugins/` and you can use standard _Express_ _(`.get()` `.post()` `.use()`, etc)_ methods to add HTTP path handlers. + +_Note: `GET /plugins/` and `POST /plugins//configure` are reserved by server (see below)._ + +It should be noted that _Express_ does not have a public API for deregistering subrouters, so `stop` does not do anything to the router. + +If a plugin does provide an API, it is strongly recommended that it provide an **OpenApi description** to document its operation. + +Doing so promotes interoperability with other plugins / webapps by making it easy to find and use the functionality built into plugins. It is also a means to avoid duplication, promote reuse and the possibility of including them in the Signal K specification. + +See [Server Plugins](server_plugin.md#add-an-openapi-definition) for details. + +--- + +### Plugin configuration HTTP API + +#### `GET /plugins/` + +Get a list of installed plugins and their configuration data. + +#### `GET /plugins/` + +Get information from an installed plugin. + +Example result: +```json +{ + "enabled": false, + "id": "marinetrafficreporter", + "name": "Marine Traffic Reporter" +} +``` + +#### `POST /plugins//configure` + +Save configuration data for a plugin. Stops and starts the plugin as a side effect. diff --git a/docs/src/develop/rest-api/anchor_api.md b/docs/src/develop/rest-api/anchor_api.md new file mode 100644 index 000000000..1972e8845 --- /dev/null +++ b/docs/src/develop/rest-api/anchor_api.md @@ -0,0 +1,20 @@ +# Anchor API + +#### (Proposed) + +_Note: The definition of this API is currently under development and the information provided here is likely to change._ + +The Signal K server Anchor API will define endpoints that can be implemented by plugins for the purposes of implementing and and operating an anchor alarm. + +A plugin that implements this API must ensure that all endpoints and operations comply with the definition to ensure applications making requests receive reliable and consistent results. + +The following HTTP requests are proposed: + +POST `/navigation/anchor/drop` (Commence lowering of anchor) + +POST `/navigation/anchor/radius` { value: number } (Set the radius of the alarm area) + +POST `/navigation/anchor/reposition` { rodeLength: number, anchorDepth: number } (Calculate anchor position) + +POST `/navigation/anchor/raise` (Commence raising the anchor) + diff --git a/docs/src/develop/rest-api/autopilot_api.md b/docs/src/develop/rest-api/autopilot_api.md new file mode 100644 index 000000000..21e5229a7 --- /dev/null +++ b/docs/src/develop/rest-api/autopilot_api.md @@ -0,0 +1,38 @@ +# Autopilot API + +#### (Under Development) + +_Note: This API is currently under development and the information provided here is likely to change._ + +The Signal K server Autopilot API will provide a common set of operations for interacting with autopilot devices and (like the Resources API) will rely on a "provider plugin" to facilitate communication with the autopilot device. + +The Autopilot API will handle requests to `/steering/autopilot` paths and pass them to an Autopilot Provider plugin which will send the commands to the autopilot device. + +The following operations are an example of the operations identified for implementation via HTTP `GET` and `PUT` requests: + +PUT `/steering/autopilot/engage` (engage / activate the autopilot) + +PUT `/steering/autopilot/disengage` (disengage / deactivate the autopilot) + +GET `/steering/autopilot/state` (retrieve the current autopilot state) + +PUT `/steering/autopilot/state` (set the autopilot state) + +GET `/steering/autopilot/mode` (retrieve the current autopilot mode) + +PUT `/steering/autopilot/mode` (set autopilot mode) + +GET `/steering/autopilot/target` (get currrent target value) + +PUT `/steering/autopilot/target` (set the target value based on the selected mode) + +PUT `/steering/autopilot/target/adjust` (increment / decrement target value) + +PUT `/steering/autopilot/tack/port` (perform a port tack) + +PUT `/steering/autopilot/tack/starboard` (perform a starboard tack) + + + +[View the PR](https://github.com/SignalK/signalk-server/pull/1596) for more details. + diff --git a/WORKING_WITH_COURSE_API.md b/docs/src/develop/rest-api/course_api.md similarity index 66% rename from WORKING_WITH_COURSE_API.md rename to docs/src/develop/rest-api/course_api.md index c0e195981..3d752164c 100644 --- a/WORKING_WITH_COURSE_API.md +++ b/docs/src/develop/rest-api/course_api.md @@ -3,29 +3,131 @@ ## Overview -The SignalK Course API provides a consistent means to perform common operations and to ensure that all related Signal K paths set with the relevant values. -It integrates with the [Resources API](WORKING_WITH_RESOURCES_PROVIDER_API.md) to retrieve information about an active route or destination. +The _Course API_ provides common course operations under the path `/signalk/v2/api/vessels/self/navigation/course` ensuring that all related Signal K data model values are maintained and consistent. This provides a set of data that can be confidently used for _course calculations_ and _autopilot operation_. -Providing an API to manage the paths under `/signalk/v2/api/vessels/self/navigation/course` ensures the data underpinning course calculations and autopilot operation is consistent and valid. +Client applications use `HTTP` requests (`PUT`, `GET`,`DELETE`) to perform operations and retrieve course data. -Client applications use `HTTP` requests to set (`PUT`), retrieve (`GET`) and clear (`DELETE`) course data. +Additionally, the Course API persists course information on the server to ensure data is not lost in the event of a server restart. -_Note: the Course API persists course information on the server to ensure data is not lost in the event of a server restart._ +_Note: You can view the _Course API_ OpenAPI definition in the Admin UI (Documentation => OpenApi)._ + +--- +## Setting a Course + +The Course API provides endpoints for: +1. Navigate to a location _(lat, lon)_ +1. Navigate to a waypoint _(href to waypoint resource)_ +1. Follow a Route _(href to a route resource)_. + +### 1. Navigate to a Location + +To navigate to a point submit a HTTP `PUT` request to `/signalk/v2/api/vessels/self/navigation/course/destination` and supply the latitude & longitude of the destination point. + +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/destination' {"position": {"latitude": -60.5, "longitude": -166.7}} +``` + + +### 2. Navigate to a Waypoint + +To navigate to a point submit a HTTP `PUT` request to `/signalk/v2/api/vessels/self/navigation/course/destination` and supply a reference to a waypoint resource entry under `/resources/waypoints` + +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/destination' {"href": "/resources/waypoints/5242d307-fbe8-4c65-9059-1f9df1ee126f"} +``` + + +### 3. Follow a Route + +To follow a route submit a HTTP `PUT` request to `/signalk/v2/api/vessels/self/navigation/course/activeRoute` and supply a reference to a route resource entry under `/resources/routes`. + +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute' {"href": "/resources/routes/5242d307-fbe8-4c65-9059-1f9df1ee126f"} +``` + +Additional parameters can be set when following a route including: +- Defining the point along the route to start at +- The direction to follow the route (forward / reverse) + +_Example: Following a route in reverse direction:_ +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute' +{ + "href": "/resources/routes/5242d307-fbe8-4c65-9059-1f9df1ee126f", + "reverse": true +} +``` + +#### Advancing along a Route + +As progress along a route is made, you can use the following endpoints to update the destination. + +To set the destination to the next point along the route: +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' +``` + +To advance the destination to a point `n` places beyond the current destination point, supply a positive number representing the number of points to advance: + +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": 2} +``` +_Sets destination to the point after the next in sequence._ + +To set the destination to the previous point along the route: +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": -1} +``` + +To set the destination to a point `n` places prior the current destination point, supply a negative number representing the number of points prior: + +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": -2} +``` +_Sets destination to the point two prior to the current destination._ + +To set the destination to a specific point along the route, supply the zero-based index of the point: + +_Example: 4th point along the route._ +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/pointIndex' {"value": 3} +``` +_Value contains the 'zero-based' index of the point along the route (i.e. 0 = 1st point, 1 = 2nd point, etc.)_ + +To reverse the direction along the route: +```typescript +HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/reverse' +``` + +#### Delta Messages + +The Course API emits delta messages with the following paths when course information has been changed. + +_Note: Delta values reflect the relevant property of the Course Information data object as detailed in the +[Retrieving Course Information](#retrieving-course-information) section._ + +- `navigation.course.startTime` +- `navigation.course.targetArrivalTime` +- `navigation.course.arrivalCircle` +- `navigation.course.activeRoute` +- `navigation.course.nextPoint` +- `navigation.course.previousPoint` -**See the [OpenAPI documentation](https://demo.signalk.io/admin/openapi/) in the Admin UI (Server => OpenApi) for more Course API details.** ## Retrieving Course Information ---- Course information is retrieved by submitting a HTTP `GET` request to `/signalk/v2/api/vessels/self/navigation/course`. ```typescript HTTP GET 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course' ``` -The response will contain values pertaining to the current course. See also [Delta Messages](#delta-messages). -_Example: Navigate to Location._ +The contents of the response will reflect the operation used to set the current course. The `nextPoint` & `previousPoint` sections will always contain values but `activeRoute` will only contain values when a route is being followed. + +#### 1. Operation: Navigate to a location _(lat, lon)_ + +_Example response:_ ```JSON { "startTime": "2023-01-27T01:47:39.785Z", @@ -49,7 +151,37 @@ _Example: Navigate to Location._ } ``` -_Example: Following a route._ +#### 2. Operation: Navigate to a waypoint _(href to waypoint resource)_ + +_Example response:_ +```JSON +{ + "startTime": "2023-01-27T01:47:39.785Z", + "targetArrivalTime": "2022-06-10T01:29:27.592Z", + "arrivalCircle": 4000, + "activeRoute": null, + "nextPoint": { + "href": "/resources/waypoints/f24d72e4-e04b-47b1-920f-66b78e7b033e", + "type": "Waypoint", + "position": { + "latitude": -34.92084502261776, + "longitude": 131.54823303222656 + } + }, + "previousPoint": { + "type":"VesselPosition", + "position": { + "latitude": -34.82084502261776, + "longitude": 131.04823303222656 + } + } +} +``` + + +#### 3. Operation: Follow a Route _(href to a route resource)_. + +_Example response:_ ```JSON { "startTime": "2023-01-27T01:47:39.785Z", @@ -101,121 +233,7 @@ _Example: Following a route._ } ``` -_Example: Navigate to Waypoint._ -```JSON -{ - "startTime": "2023-01-27T01:47:39.785Z", - "targetArrivalTime": "2022-06-10T01:29:27.592Z", - "arrivalCircle": 4000, - "activeRoute": null, - "nextPoint": { - "href": "/resources/waypoints/f24d72e4-e04b-47b1-920f-66b78e7b033e", - "type": "Waypoint", - "position": { - "latitude": -34.92084502261776, - "longitude": 131.54823303222656 - } - }, - "previousPoint": { - "type":"VesselPosition", - "position": { - "latitude": -34.82084502261776, - "longitude": 131.04823303222656 - } - } -} -``` - - -## Setting a Course ---- - -The Course API provides endpoints for: -1. Navigating to a point. -1. Following a Route _(reference to a route entry under `/resources/routes`)_ - - -### 1. Navigating to a Point - -To navigate to a point submit a HTTP `PUT` request to `/signalk/v2/api/vessels/self/navigation/course/destination` and supply either: -- The latitude & longitude of the destination point -- A reference to a waypoint entry under `/resources/waypoints` - -_Example: Setting destination using lat / lon:_ -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/destination' {"position": {"latitude": -60.5, "longitude": -166.7}} -``` - -_Example: Setting waypoint as destination:_ -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/destination' {"href": "/resources/waypoints/5242d307-fbe8-4c65-9059-1f9df1ee126f"} -``` - -### 2. Following a Route - -To follow a route submit a HTTP `PUT` request to `/signalk/v2/api/vessels/self/navigation/course/activeRoute` and supply a reference to a route entry under `/resources/routes`. - -_Example: Following a route:_ -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute' {"href": "/resources/routes/5242d307-fbe8-4c65-9059-1f9df1ee126f"} -``` - -Additional parameters can be set when following a route including: -- Defining the point along the route to start at -- The direction to follow the route (forward / reverse) - -_Example: Following a route in reverse direction:_ -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute' -{ - "href": "/resources/routes/5242d307-fbe8-4c65-9059-1f9df1ee126f", - "reverse": true -} -``` - -### Advancing along a Route - -As progress along a route is made, you can use the following endpoints to update the destination. - -To set the destination to the next point along the route: -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' -``` - -To advance the destination to a point `n` places beyond the current destination point, supply a positive number representing the number of points to advance: - -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": 2} -``` -_Sets destination to the point after the next in sequence._ - -To set the destination to the previous point along the route: -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": -1} -``` - -To set the destination to a point `n` places prior the current destination point, supply a negative number representing the number of points prior: - -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/nextPoint' {"value": -2} -``` -_Sets destination to the point two prior to the current destination._ - -To set the destination to a specific point along the route, supply the zero-based index of the point: - -_Example: 4th point along the route._ -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/pointIndex' {"value": 3} -``` -_Value contains the 'zero-based' index of the point along the route (i.e. 0 = 1st point, 1 = 2nd point, etc.)_ - -To reverse the direction along the route: -```typescript -HTTP PUT 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/activeRoute/reverse' -``` - ## Cancelling navigation ---- To cancel the current course navigation and clear the course data @@ -223,63 +241,13 @@ To cancel the current course navigation and clear the course data HTTP DELETE 'http://hostname:3000/signalk/v2/api/vessels/self/navigation/course/' ``` - -## Delta Messages --- -The Course API emits the delta messages with the following paths when course information has been changed. - -_Note: Delta values reflect the relevant property of the Course Information data object as detailed [above](#retrieving-course-information)._ - -- `navigation.course.startTime` -- `navigation.course.targetArrivalTime` -- `navigation.course.arrivalCircle` -- `navigation.course.activeRoute` -- `navigation.course.nextPoint` -- `navigation.course.previousPoint` - - ## Course Calculations ---- -The Course API defines the path `/vessels/self/navigation/course/calcValues` to accommodate the calculated values related to course navigation. +Whilst not performing course calculations, the _Course API_ defines the paths to accommodate the values calculated during course navigation. -_**Note: The Course API implementation on the server does not perform the calculations to populate these values!**_ +Click [here](./course_calculations.md) for details. -The following values are defined to be populated by a course computer / plugin based on the Course Information populated by the Course API. -_Path: `navigation/course/calcValues`_ -``` -calcMethod: "Rhumbline" or "GreatCircle" -crossTrackError -bearingTrackTrue -bearingTrackMagnetic -estimatedTimeOfArrival e.g. "2022-04-22T05:02:56.484Z" -distance -bearingTrue -bearingMagnetic -velocityMadeGood -timeToGo -targetSpeed -previousPoint: { distance } -``` -_Example:_ -``` -{ - "calcMethod": "Rhumbline", - "crossTrackError": 458.784, - "bearingTrackTrue": 4.58491, - "bearingTrackMagnetic": 4.51234, - "estimatedTimeOfArrival": "2022-04-22T05:02:56.484Z", - "distance": 10157, - "bearingTrue": 4.58491, - "bearingMagnetic": 4.51234, - "velocityMadeGood": 7.2653, - "timeToGo": 8491, - "targetSpeed": 2.2653, - "previousPoint": { - "distance": 10157 - } -} -``` diff --git a/docs/src/develop/rest-api/course_calculations.md b/docs/src/develop/rest-api/course_calculations.md new file mode 100644 index 000000000..4ce631ba6 --- /dev/null +++ b/docs/src/develop/rest-api/course_calculations.md @@ -0,0 +1,70 @@ +# Course Calculations and Providers + +The _Course API_ defines the path `/vessels/self/navigation/course/calcValues` to accommodate the calculated values related to course navigation. + +These paths are available to be populated by a "course provider" plugin that uses the course information set using the _Course API_. This approach promotes the extensibility of Signal K server providing flexibility and interoperability. See [Course Provider Plugins](#course-provider-plugins) below. + + + +## Calculated value paths: `calcValues` + +The following paths are defined to hold values calculated using the information maintained by _Course API_ operations: + +- calcMethod: _("Rhumbline" or "GreatCircle")_ +- crossTrackError +- bearingTrackTrue +- bearingTrackMagnetic +- estimatedTimeOfArrival _(e.g. "2022-04-22T05:02:56.484Z")_ +- distance +- bearingTrue +- bearingMagnetic +- velocityMadeGood +- timeToGo +- targetSpeed +- previousPoint.distance + + +_Example:_ +``` +{ + "calcMethod": "Rhumbline", + "crossTrackError": 458.784, + "bearingTrackTrue": 4.58491, + "bearingTrackMagnetic": 4.51234, + "estimatedTimeOfArrival": "2022-04-22T05:02:56.484Z", + "distance": 10157, + "bearingTrue": 4.58491, + "bearingMagnetic": 4.51234, + "velocityMadeGood": 7.2653, + "timeToGo": 8491, + "targetSpeed": 2.2653, + "previousPoint": { + "distance": 10157 + } +} +``` + +## Course Notifications + +Calculated course values that cross a threshold should trigger a notification so that the necessary action can be taken. + +The Course API defines the following notifications which should be implemented by a course provider: +- `navigation.course.arrivalCircleEntered` +- `navigation.course.perpendicularPassed` + + +## Course Provider Plugins + +Signal K server includes the `Course Data Provider` plugin as part of the installation to provide out-of-the-box support for course calculations nd notifications. + +This plugin can be replaced with others from the AppStore, or your own, to extend the number and types of calculations performed. + +If you are looking to develop a course provider plugin, following are the recommended guidlines: +1. Ensure values are generated for ALL the defined paths (above) +1. Values MUST be calculated using `/vessels/self/navigation/course` path values mainntained by the _Course API_ +1. Ensure values are set to null when no destination is set or the value cannot be calculated +1. Perform the calculations using a "worker thread" to minimise impact on the server "main thread" +1. Ensure the worker is set up and shut down as part of plugin "start" and "stop" functions +1. Raise the notifications outlined above. + + diff --git a/docs/src/develop/rest-api/notifications_api.md b/docs/src/develop/rest-api/notifications_api.md new file mode 100644 index 000000000..49ded246c --- /dev/null +++ b/docs/src/develop/rest-api/notifications_api.md @@ -0,0 +1,18 @@ +# Notifications API + +#### (Under Development) + +_Note: This API is currently under development and the information provided here is likely to change._ + +The Signal K server Notifications API will provide a set of operations for raising, actioning and clearing notifications. + +It will implement: + +- Both HTTP endpoints for interactive use (`/signalk/v2/api/notifications`) and an interface for use by plugins and connection handlers to ensure effective management of notifications. + +- The ability to action notifications (e.g. acknowledge, silence, etc) and preserve the resulting status so it is available to all connected devices. + +- A unique `id` for each notification which can then be used to action it, regardless of the notification source. + +[View the PR](https://github.com/SignalK/signalk-server/pull/1560) for more details. + diff --git a/docs/src/develop/rest-api/open_api.md b/docs/src/develop/rest-api/open_api.md new file mode 100644 index 000000000..82f5c6f02 --- /dev/null +++ b/docs/src/develop/rest-api/open_api.md @@ -0,0 +1,29 @@ +# REST APIs + +REST APIs were introduced in Signal K server version 2 to provide a way for applications and plugins perform operations and ensure that the Signal K data model is consistent. + +The OpenAPI definitions can be found under _Documentation -> OpenAPI_ in the server Admin UI. + + +### APIs available in Signal K server v2.0.0 and later: + +APIs are available via `/signalk/v2/api/` + +| API | Description | Endpoint | +|--- |--- |--- | +| [Course](./course_api.md) | Set a course, follow a route, advance to next point, etc. | `vessels/self/navigation/course` | +| [Resources](./resources_api.md) | Create, view, update and delete waypoints, routes, etc. | `resources` | + +--- + + +#### Following is a list of proposed APIs for implementation: + + +| Proposed API | Description | Endpoint | +|--- |--- |--- | +| _[`Notifications`](notifications_api.md)_ | Provide the ability to raise, update and clear notifications from multiple sources. _[View PR](https://github.com/SignalK/signalk-server/pull/1560)_| `notifications` | +| _[`Autopilot`](./autopilot_api.md)_ | Provide the ability to send common commands to an autopilot via a provider plugin. _[View PR](https://github.com/SignalK/signalk-server/pull/1596)_ | `vessels/self/steering/autopilot` | +| _[`Anchor`](./anchor_api.md)_ | Provide endpoints to perform operations and facilitate an anchor alarm. | `vessels/self/navigation/anchor` | + +--- diff --git a/WORKING_WITH_RESOURCES_API.md b/docs/src/develop/rest-api/resources_api.md similarity index 93% rename from WORKING_WITH_RESOURCES_API.md rename to docs/src/develop/rest-api/resources_api.md index 832903d71..0c33d68cc 100644 --- a/WORKING_WITH_RESOURCES_API.md +++ b/docs/src/develop/rest-api/resources_api.md @@ -5,9 +5,9 @@ The SignalK specification defines a number of resources (routes, waypoints, notes, regions & charts) each with its own path under the root `resources` path _(e.g. `/signalk/v2/api/resources/routes`)_. -The SignalK server validates requests to these resource paths and passes them to a [Resource Provider plugin](RESOURCE_PROVIDER_PLUGINS.md) for storage and retrieval. +Additionally, the `/resources` path can be used to host other user defined resource types, each grouped within its own folder _(e.g. `/signalk/v2/api/resources/fishingZones`)_. - _You can find plugins in the `App Store` section of the server admin UI._ +The _Resources API_ validates requests to these resource paths and passes them to a [Resource Provider plugin](../plugins/resource_provider_plugins.md) for storage and retrieval. Client applications can then use `HTTP` requests to these paths to store (`POST`, `PUT`), retrieve (`GET`) and remove (`DELETE`) resource entries. @@ -110,7 +110,7 @@ _Note: the submitted resource data is validated against the OpenApi schema defin --- ## Multiple Providers for a Resource Type -The ResourcesAPI will allow for multiple plugins to register as a provider fo a resource type. +The Resources API will allow for multiple plugins to register as a provider for the same resource type. When this scenario occurs the server services request in the following ways: diff --git a/docs/src/develop/webapps.md b/docs/src/develop/webapps.md new file mode 100644 index 000000000..35f463eef --- /dev/null +++ b/docs/src/develop/webapps.md @@ -0,0 +1,89 @@ +# WebApps and Components + +## Introduction + +Signal K Server provides the following ways to add web-based user interfaces to enhance functionality and usability: + +1. **Standalone WebApps** are web applications that when launched, the server Admin UI disappears and the webapp controls the whole page (browser window / tab). + +1. **Embedded WebApps** are web applications that when launched, are **embedded in the server Admin UI**, leaving the toolbar and menu available to the user. +![vesselpositions](../img/vesselpositions.png "Vesselpositions Embedded Webapp") + +1. **Embedded Plugin Configuration Forms** are forms provided by a plugin that the server embeds within the _Plugin Config_ screen to replace the generic form rendered using the plugin _configuration schema_. This allows a richer set of controls to be provided for the user to configure the plugin compared to the more generice server generated form provides. +![calibration](../img/calibration.png "Calibration plugin configuration form") + +1. **Embedded Components** are individual UI components provided by a plugin or a webapp. They are listed in the _Addons_ section at the bottom of the _Webapps_ page of the Admin UI. More a concept than a fully implemented feature at this stage, the idea is to allow a plugin to add individual components to different parts of the server UI. + +All Plugins, WebApps and Components can be installed via the _Appstore_. + +## WebApp Structure + +All WebApps (like plugins) are installed with `npm`, either from the npm registry or from your own Github repository. Only WebApps that are relevant for all users should be published to `npm` to be made available in the _Appstore_ of all Signal K Servers. + +_Note: Private plugins need not be published to `npm` - see the documentation for [npm install](https://docs.npmjs.com/cli/v6/commands/npm-install) for details._ + + +The basic structure of a webapp is: +- A folder named `public` that contains the html, JavaScript and resource files such as images, fonts and style sheets. This folder is automatically mounted by the server so that the webapp is available after installation and the server restarted. +- `package.json` containing special keywords that classifies the webapp: + - `signalk-webapp` - standalone webapp + - `signalk-embeddable-webapp` - embeddable webapp + - `signalk-plugin-configurator` - plugin configuration form + +This structure is all that is needed for a standalone webapp. + +You can also include the following section in `package.json` to control how your webapp appears in the _Webapps_ list: +```JSON + "signalk": { + "appIcon": "./assets/icons/icon-72x72.png", + "displayName": "Freeboard-SK" + }, +``` + +where: +- `appIcon` is the path (relative to the `public` directory) to an image within the package to display in the webapp list. The image should be at least 72x72 pixels in size. +- `displayName` is the text you want to appear as the name in the webapp list. _(By default the _name_ attribute in the `package.json` is used.)_ + +See also [Working Offline](./developer_notes.md#offline-use). + + +## Embedded Components and Admin UI / Server interfaces + +Embedded components are implemented using [Webpack Federated Modules](https://webpack.js.org/concepts/module-federation/) and [React Code Splitting](https://reactjs.org/docs/code-splitting.html). + +_Note: There is no keyword for a module that provides only embedded components, use `signalk-webapp` instead._ + +You need to configured Webpack to create the necessary code for federation using *ModuleFederationPlugin* and expose the component with fixed names: +- embeddable webapp: `./AppPanel` +- plugin configuration form: `./PluginConfigurationPanel` +- embedded component: `./AddonPanel` + +The ModuleFederationPlugin library name must match the package name and be a "safe" name for a webpack module like in `library: { type: 'var', name: packageJson.name.replace(/[-@/]/g, '_') },` + +The exposed modules need to `export default` a React component - both class based components and stateless functional components can be used. The server dependencies like `reactstrap` can and should be used. Add `@signalk/server-admin-ui-dependencies` as a dependency to the webapp, it defines the depedencies used by the server admin UI. + +See the vesselpositions embedded webapp/component and Calibration plugin for examples of each. It is probably easier to start with either one and modify them to suit your needs. Don't forget to change the module id and name in package.json! + + +## WebApp / Component and Admin UI / Server interfaces + +Standalone WebApps can use the server's APIs _(Signal K http and WebSocket APIs as well as any server specific endpoints)_ but they need to implement everything else themselves. + +Embedded WebApps, Components and Plugin Configuration Forms work inside the Admin UI, so they can interact with both the Admin UI and the server using APIs exposed by the Admin UI as component properties. + + +Embedded webapp properties: +- access to the login status of the browser user +- ability to render Login form instead of the webapp content +- getting and setting application data +- opening an automatically reconnecting WebSocket connection to the server +- getting Signal K data via `get` +- [Embedded](https://github.com/SignalK/signalk-server/blob/master/packages/server-admin-ui/src/views/Webapps/Embedded.js) + +PluginConfigurationForm properties: +- `configuration` : the configuration data of the plugin +- `save`: function to save the configuration data +- [EmbeddedPluginConfigurationForm](https://github.com/SignalK/signalk-server/blob/master/packages/server-admin-ui/src/views/Configuration/EmbeddedPluginConfigurationForm.js) + + +**_Note: The documentation regarding embedded WebApps and Components provided at this time is rudimentary and should be considered under development as the concept is evolving._** \ No newline at end of file diff --git a/docs/src/features/anchoralarm/alarmsilencer.jpg b/docs/src/features/anchoralarm/alarmsilencer.jpg new file mode 100644 index 000000000..f61e67890 Binary files /dev/null and b/docs/src/features/anchoralarm/alarmsilencer.jpg differ diff --git a/docs/src/features/anchoralarm/anchor_alarm_plugin_ui.png b/docs/src/features/anchoralarm/anchor_alarm_plugin_ui.png new file mode 100644 index 000000000..818f0941a Binary files /dev/null and b/docs/src/features/anchoralarm/anchor_alarm_plugin_ui.png differ diff --git a/docs/src/features/anchoralarm/anchoralarm.md b/docs/src/features/anchoralarm/anchoralarm.md new file mode 100644 index 000000000..1a2fda34e --- /dev/null +++ b/docs/src/features/anchoralarm/anchoralarm.md @@ -0,0 +1,334 @@ +# Feature: Anchor Alarm + + +## Contents + +- [Prerequisites](#prerequisites) +- [Configuration](#configuration) +- [Using the Anchor Alarm](#using-the-anchor-alarm) +- [Alarms & Notifications](#alarms--notifications) +- [Alternatives to WilhelmSK](#alternatives-to-wilhelmsk) + +## Introduction + +This document describes how to setup an anchor alarm using Signal K Server and the WilhelmSK app that: +- Uses the GPS data from your navigation system, not your phone +- Has a graphical map + satellite view +- Vessel movement is saved and displayed as Track on a map +- Provides the ability to move the anchor location +- Sends push notifications to your iPhone (not Android), both remotely via the Internet and locally on the boat +- Sound an audible alarm on a Fusion stereo or via a common speaker connected to a Yacht Devices N2K device +- Does not depend on your phone having enough battery, GPS reception to wake you up + + + + +_Image: WilhelmSK App on iPad._ + +## Prerequisites + +- SignalK server installed, for example on a Victron GX device or RaspberryPi. See [Installation](../../installation/install.md) for details. +- [WilhelmSK iOS App](https://itunes.apple.com/us/app/wilhelmsk/id1150499484?mt=8) + +For use with other Apps (Android, web apps, etc) see [Alternatives to WilhelmSK](#alternatives-to-wilhelmsk). + + +## Configuration + +**Step 1.** Configure the Vessel data in SignalK server: + + +Be sure to set: +- Boat length +- GPS Distance from Bow +- GPS Distance from Center. + +#### Step 2. Install required Signal K Server plugins: + + +- Open the Signal K Admin UI and if necessary _Login_ +- Select _Appstore -> Available_ from the menu +- Under _Available Apps_ select _All_ from the dropdown + + + +Locate and install each of the following plugins: +- **signalk-anchoralarm-plugin** +- **signalk-push-notifications** +- **signalk-alarm-silencer** +- **@signalk/tracks-plugin** + +After installation is complete, restart the server. + +_Note: The _Restart_ button only appears at the upper right of the screen when security has been enabled. (See [Enabling Security](../../security.md#enabling-security) for details.)_ + + +#### Step 3 Configure the plugins: + +Each of the plugins you installed in the previous step need to be configured and enabled. To do this: + +- Select `Server -> Plugin Config`` from the menu +- Locate the plugin +- Set the appropriate configuration values +- Click _Submit_. +- Ensure the plugin is _Enabled_. + +Following are details of how to configure each plugin: + +**_Anchor Alarm plugin:_** + +This plugin only needs to be enabled. + +No configuration is required as the settings values are populated by WilhelmSK. + + + + +**_Alarm Silencer plugin:_** + +This plugin only needs to be enabled. + +No configuration options available. + + + + +**_Push Notifications plugin:_** + +- To send notifications via an Internet connection check _Enable Remote Push_. + +_Note: The WilhelmSK app uses an Amazon RDS service to deliver push notifications. No additional accounts, etc are required as they are included with purchase._ + +- _Local Push SSIDs_: Enter the SSID(s) of the WiFi network(s) on your boat which the devices that are to receive notifications are connected. + +- _Local Push Port_ specifies the port number that WilhelmSK app will receive the local push notifications. +The default value is _3001_ and only needs to be changed if that port number is already in use by another app / plugin on the Signal K Server. + +_Note: It is not necessary to set the same value in the WilhelmSK app, it auto configures itself._ + + + + +**_Tracks plugin_** + +This plugin keeps track of the position of vessels visible to the Signal K Server. This enables apps to display a track of vessel movements. + +The configuration values chosen will determince the amount of resource required run the plugin (and potentially the performance of the Signal K Server) so please consider this when making entries. + +The following settings represent conservative values that are suitable for use with an anchor alarm: +- Track resolution: 60000 (60 seconds) +- Points to keep: 240 (4 hours) +- Max idle time: 600 (10 minutes) +- Max Radius: 1 meter (no other vessels). + +_Note: This plugin stores the track all the time, not only when on anchor. Which comes in handy when you enable the the anchor alarm a while after having dropped the anchor._ + + + + +**_Step 4._** Connect WilhelmSK to your SignalK server + +After installing the WilhelmSK app, go to the iOS settings screen and check that it has network access permissions. + + + +Also ensure that Background App Refresh also enabled as it is required for local push notifications and to raise an alarm while WilhelmSK app in not open on your phone or tablet. + +Once network access has been enabled, start the WilhelmSK app and go to the connections menu. + + + +Your boat's name _(as entered in the vessel data, in step 1 above)_ and suffixed with (Discovered) should appear in the list. _(If you can’t see it please ensure WilhelmSK has been granted network access permissions.)_ + +Select your boat from the list and the configuration screen is displayed: + + + +- Enter the user name and password that you use when logging into Signal K Server. + +- Select _AutoConnect On This Network_. This will cause WilhelmSK to auto-select this connection when on the currently connected WiFi network. + +With regards to the other available config options: +- _Request Admin Access_ provides an alternative to using user name and password using a security token instead. Selecting this option sends an [Access Request](../../setup/generating_tokens.md#access-requests) to the SignalK Server. The request will then need to be approved in the _Security -> Access Requests_ screen of the Signal K Admin UI . +- _Port_: 3000 is the standard port. +- _AutoConnect Default_ is used for a remote connection, which is out of scope of this manual. + +Finally, verify your connection. Swipe left to back to the main menu and select the same connection: + + + + +**_Step 5._** Enable WilhelmSK to receive push notifications + +From the _Settings_ menu select _Notifications_. + + + +Note that the actual monitoring of the GPS coordinates is done by Signal K Server itself, not the WilhelmSK app. The app is only used to configure the anchor location, enable the alarm and to receive the alarm notifications. + +It is also possible, and highly recommended, to add other notifications. For example via Fusion stereo, if installed, or by using a dry contact with audible alarm. _(These are out of scope of this manual.)_ + +_Tip: For using the Fusion stereo: search the server plugins with the keyword Fusion._ + + + +## Using the Anchor Alarm + +Now that the connections have been configured, go to the main screen of the WilhelmSK App where you’ll see something similar to the following screenshot. + +If data is being received from the Signal K server, green dots are displayed in the top right of each gauge to indicate that the value displayed is current. + + + +In case of incorrect username and/or password or other connection errors, these will be displayed at the top of the screen as warning. + + + + +With an established connection and data being received, swipe to the right a few times to display the anchor watch page. + + + + +Clicking the anchor on the top left of screen (next to the menu button), cycles through the sequence of arming / disarming the alarm: + +- White anchor indicates disarmed. Click when dropping the anchor and the color will change to yellow. +- Yellow anchor indicates that the rode length is being calculated. Click when you have finished letting out the anchor and the colour will change to green. +- Green anchor means the alarm is armed and ready. + +Use the “four arrows icon” on the top left of screen to move the anchor location. + +You can also change the alarm radius. + +Clicking the green anchor will prompt you to confirm that you want to disarm the alarm / raise the anchor. + + +_TIP: To return to other pages (i.e. COG and AWA gauges) swipe left by holding the Course up/Head up/North up menu._ + +--- + +## Alarms & Notifications + +For most use cases, push notifications shouldn’t be the only means of receiving alarms as the phone can decide to hide notifications, etc so additional means of sounding the alarm should be employed. + +Following are some alternatives. + +### Audible alarms using the Raspberry Pi audio connector + +To have the Signal K Server produce audible notifications using a speaker connected directly to the Raspberry Pi audio connector install the [signalk-audio-notifications](https://www.npmjs.com/package/@meri-imperiumi/signalk-audio-notifications) plugin. + + +### Alarm to NMEA2000 network + +To have the Signal K Server forward the alarm to the NMEA2000 network to appear on connected devices, install and configure the [signalk-to-nmea2000](https://www.npmjs.com/package/signalk-to-nmea2000) plugin. + + +#### Raymarine alarm compatibility + +With the Raymarine options enabled in the _signalk-to-nmea2000_ plugin, an audible alarm will be generated on the Raymarine SeaTalkNG Auxiliary Alarm Buzzer - A8061 and will display on Raymarine Axiom plotters. + +_Note: The anchor alarm will be displayed as a “Shallow anchor alarm” on Raymarine, as that is the most suitable alarm type. Interpret that as just an anchor alarm, which has nothing to do with shallow. Here is how it looks on the Raymarine plotter: + + + +#### Garmin alarm compatibility + +Compatible + +#### Navico (B&G, Simrad, Lowrance) alarm compatibility + +Not compatible + +#### Yacht Devices compatibility + +The [YDAB-01](https://www.yachtd.com/products/alarm_button.html) is a very flexible and configurable device sold by Yacht Devices. + +It is connected to the NMEA2000 network and has a 10W audio amplifier which can output sound via a 4 or 8 Ohm speaker which directly connected. It has bank of 28 sound signals can be configured to sound on receipt of the anchor alarm. + +#### Additional information + +The following post on Panbo is a great resource with regards to the N2K Alert PGNs: +[Link](https://panbo.com/nmea-2000-network-alert-pgns-seem-great-so-why-are-they-hardly-used/) + +Thread on Signal K Slack where most of the information for this chapter was gathered: +[Link](https://signalk-dev.slack.com/archives/C02EMP1RF/p1693086271207619) + +### Remote notifications + +There are a number of Signal K Server plugins which enable notifications to be sent remotely, following are just a few: + +#### signalk-pushover-notification-relay + +This plugin listens for a change of state in Signal K notifications and sends the updates to the Pushover App which is available for both Apple and Android phones and tablets. + +This is a paid service from [Pushover](https://pushover.net/). + +_Plugin:_ [signalk-pushover-notification-relay](https://www.npmjs.com/package/signalk-pushover-notification-relay) + +#### signalk-notifications-manager + +This plugin also works with [Pushover](https://pushover.net/) to deliver notifications as push messages on your phone or tablet, but it also: +- Keeps a history of alerts stored in a local database +- Includes a Signal K web app to manage the notifications. + +_Plugin:_ [signalk-notifications-manager](https://www.npmjs.com/package/signalk-notifications) + +#### signalk-clicksend-notifications-relay + +This plugin forwards notifications via the (paid) Clicksend SMS gateway, which can deliver notifications to your phone. + +_Plugin:_ [signalk-clicksend-notifications-relay](https://www.npmjs.com/package/signalk-clicksend-notification-relay) + +### Switching a relay + +Operating a switch or relay provides a range of options for sounding an alarm but will require diving a bit deeper into the various (and extensive) automation options made available by Signal K Server. + +A good place to start is the [signalk-switch-automation](https://github.com/sbender9/signalk-switch-automation) plugin. + + +--- + +## Alternatives to WilhelmSK + +As the data processing to "watch" the anchor and generate the alarm messages is performed by the Signal K Server, +client applications, any client application that supports the _anchor-alarm_ plugin can be used to arm / disarm and configure it. + +Listed below are some other apps and supported operations: + +| Client App | Arm / disarm | Set radius | Move location | See track | Plays sound | +| ----------- | ------------ | -------------- | -------------- | ----------- | ----------- | +|**Wilhelm SK**
(iOS) | yes | yes | yes | yes | yes | +|**Anchor alarm plugin**
(web app) | yes | yes | no | no | no | +|**Freeboard SK**
(web app) | yes | yes | no | yes | yes | +|**Aqua Map**
(iOS & Android) | ? | ? | ? | ? | ? | + + + +### Anchor Alarm Plugin + +The anchor alarm plugin provides a web user interface available under _WebApps_ in the Signal K Server Admin UI. + +It provides the ability to arm / disarm and set the radius of the anchor alarm but does not display the alarm on screen or play a sound. + + + + +### Freeboard SK + +Freeboard SK provides the ability to arm / disarm and set the radius of the anchor alarm via its _Anchor Watch_ function. +Additionally it will: +- Display the alarm on screen +- Play a sound +- Display a track. + +It is available under _WebApps_ in the Signal K Server Admin UI. + + + + + + + + + + diff --git a/docs/src/features/anchoralarm/anchoralarmplugin.jpg b/docs/src/features/anchoralarm/anchoralarmplugin.jpg new file mode 100644 index 000000000..27191b691 Binary files /dev/null and b/docs/src/features/anchoralarm/anchoralarmplugin.jpg differ diff --git a/docs/src/features/anchoralarm/appstore_available.png b/docs/src/features/anchoralarm/appstore_available.png new file mode 100644 index 000000000..e4b19e685 Binary files /dev/null and b/docs/src/features/anchoralarm/appstore_available.png differ diff --git a/docs/src/features/anchoralarm/connections.jpg b/docs/src/features/anchoralarm/connections.jpg new file mode 100644 index 000000000..302c6f266 Binary files /dev/null and b/docs/src/features/anchoralarm/connections.jpg differ diff --git a/docs/src/features/anchoralarm/freeboardsk_anchor_alarm.png b/docs/src/features/anchoralarm/freeboardsk_anchor_alarm.png new file mode 100644 index 000000000..97fc5a68d Binary files /dev/null and b/docs/src/features/anchoralarm/freeboardsk_anchor_alarm.png differ diff --git a/docs/src/features/anchoralarm/freeboardsk_anchor_watch.png b/docs/src/features/anchoralarm/freeboardsk_anchor_watch.png new file mode 100644 index 000000000..f8865d8df Binary files /dev/null and b/docs/src/features/anchoralarm/freeboardsk_anchor_watch.png differ diff --git a/docs/src/features/anchoralarm/mfd_notification.png b/docs/src/features/anchoralarm/mfd_notification.png new file mode 100644 index 000000000..ea3932f0b Binary files /dev/null and b/docs/src/features/anchoralarm/mfd_notification.png differ diff --git a/docs/src/features/anchoralarm/pushnotificationplugin.jpg b/docs/src/features/anchoralarm/pushnotificationplugin.jpg new file mode 100644 index 000000000..71caa2af1 Binary files /dev/null and b/docs/src/features/anchoralarm/pushnotificationplugin.jpg differ diff --git a/docs/src/features/anchoralarm/tracks.jpg b/docs/src/features/anchoralarm/tracks.jpg new file mode 100644 index 000000000..e0f2f373d Binary files /dev/null and b/docs/src/features/anchoralarm/tracks.jpg differ diff --git a/docs/src/features/anchoralarm/vesseldata.png b/docs/src/features/anchoralarm/vesseldata.png new file mode 100644 index 000000000..b3171c422 Binary files /dev/null and b/docs/src/features/anchoralarm/vesseldata.png differ diff --git a/docs/src/features/anchoralarm/wsk.png b/docs/src/features/anchoralarm/wsk.png new file mode 100644 index 000000000..42c0ea99c Binary files /dev/null and b/docs/src/features/anchoralarm/wsk.png differ diff --git a/docs/src/features/anchoralarm/wsk_connection.jpg b/docs/src/features/anchoralarm/wsk_connection.jpg new file mode 100644 index 000000000..3bf50606f Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_connection.jpg differ diff --git a/docs/src/features/anchoralarm/wsk_connections.jpg b/docs/src/features/anchoralarm/wsk_connections.jpg new file mode 100644 index 000000000..6f693e78b Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_connections.jpg differ diff --git a/docs/src/features/anchoralarm/wsk_error.jpg b/docs/src/features/anchoralarm/wsk_error.jpg new file mode 100644 index 000000000..5175e40b9 Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_error.jpg differ diff --git a/docs/src/features/anchoralarm/wsk_ios.jpg b/docs/src/features/anchoralarm/wsk_ios.jpg new file mode 100644 index 000000000..378ee8d76 Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_ios.jpg differ diff --git a/docs/src/features/anchoralarm/wsk_notifications.jpg b/docs/src/features/anchoralarm/wsk_notifications.jpg new file mode 100644 index 000000000..7fa376cbf Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_notifications.jpg differ diff --git a/docs/src/features/anchoralarm/wsk_phone.png b/docs/src/features/anchoralarm/wsk_phone.png new file mode 100644 index 000000000..6bb006b27 Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_phone.png differ diff --git a/docs/src/features/anchoralarm/wsk_screen.png b/docs/src/features/anchoralarm/wsk_screen.png new file mode 100644 index 000000000..f2bcbf207 Binary files /dev/null and b/docs/src/features/anchoralarm/wsk_screen.png differ diff --git a/docs/src/features/navdataserver/enable0183.png b/docs/src/features/navdataserver/enable0183.png new file mode 100644 index 000000000..bf53297dd Binary files /dev/null and b/docs/src/features/navdataserver/enable0183.png differ diff --git a/docs/src/features/navdataserver/n2kais2nmea0183.png b/docs/src/features/navdataserver/n2kais2nmea0183.png new file mode 100644 index 000000000..b27dcfd73 Binary files /dev/null and b/docs/src/features/navdataserver/n2kais2nmea0183.png differ diff --git a/docs/src/features/navdataserver/navdataserver.md b/docs/src/features/navdataserver/navdataserver.md new file mode 100644 index 000000000..db9a9bdb2 --- /dev/null +++ b/docs/src/features/navdataserver/navdataserver.md @@ -0,0 +1,103 @@ +# Signal K Server as a NMEA0183 Data Server + +## Introduction + +This document details how to setup Signal K Server to read AIS and navigation data from a NMEA2000 (or any other) network and make it available on a WiFi network for use with popular phone / tablet apps like: +- Navionics +- iSailor +- iNavX +- Aqua Map +- WilhelmSK. + +The Signal K Server does this by passing the data received from the configured Data Connections, through two plugins which transform it to NMEA0183 sentences, which are then transmitted on the WiFi network. + +## Prerequisites + +A Signal K Server: +- Installed on a suitable device (e.g. Victron GX, RaspberryPi, etc). _See [Installation](../../installation/install.md) for details._ +- Connected to a NMEA2000, etc network and Data Connections configured. +- Connected to a WiFi network. _(Beyond the scope of this document.)_ + + +## Server Setup and Configuration + +All configuration is done from within the Signal K Server Admin UI. Open the Admin UI in your browser and complete the following steps. + +#### *Step 1.* Enter Vessel Base Data: + +- Select _Server -> Setup_ from the menu. + +- The **Vessel Base Data** section, enter values for _Length_, _GPS Distance from Bow_ and _GPS Distance from Center_. + + + + + + +#### *Step 2.* Install and Configure Plugins: + +The following plugins are required to process and transform the received data to NMEA0183: + +- `signalk-to-nmea0183` ([documentation](https://www.npmjs.com/package/@signalk/signalk-to-nmea0183) on npmjs) +- `signalk-n2kais-to-nmea0183` ([documentation](https://www.npmjs.com/package/signalk-n2kais-to-nmea0183) on npmjs) + +_Note: Starting with Signal K Server version 2.2.0 these plugins are pre-installed. It is recommended that these plugins be updated to the current version._ + +**To install the plugins:** +- In the Admin UI, select _Appstore -> Available-> All_ from the menu +- Locate the `signalk-to-nmea0183`plugin and click the install icon +- Locate the `signalk-n2kais-to-nmea0183` plugin and click the install icon +- Restart the Signal K Server. + +_Note: If the **Restart** button is not displayed in the upper right of screen, refer to [Enabling Security](../../security.md#enabling-security) for details._ + +**To configure the plugins:** + +- In the Admin UI, select _Server -> Plugin Config_ from the menu. +- Locate `signalk-to-nmea0183` plugin. + 1. Ensure it is enabled + 2. Check ALL the boxes to transmit all NMEA0183 sentence types + 3. Leave the transmission interval (throttle) at 0 + 4. Click the **Submit** button to save your choices. + + + +- Locate `signalk-n2kais-to-nmea0183` plugin. This plugin transmits all the AIS targets. + 1. Ensure it is enabled + 2. Enter _nmea0183out_ in the **NMEA 0183 Out Events** field. + 4. Click the **Submit** button to save the configuration. + + + + +#### *Step 3.* Enable NMEA0183 on the WiFi network: + +- In the Admin UI, select _Server -> Settings_ +- Locate the **Server Settings** section +- Ensure that **NMEA 0183 over TCP (10110)** is set to **On** +- Click **Save** and **Restart** the Signal K Server. + + + +Once restarted the Signal K Server will transmit NMEA0183 sentences containing all your navigation and available AIS target data on the WiFi network on TCP port 10110. + +_Note: It is recommended that **Signal K over TCP (8375)** is set to **On** if you are using an app (e.g. Aqua Map) which supports the “Signal K over TCP” protocol as it is more feature rich._ + + + +## Configuring Apps + +Device apps can now be configured to connect to the NMEA0183 data stream from the Signal K Server. + +Following is an example of how to connect the Navionics boating app. + +- Open the menu +- Select **Paired devices** and add a new paired device +- Enter a name for the device _e.g. MySignalK Server_ +- In **Host** enter the IP address of the Signal K Server you configured in the steps above +- In **Port** enter _10110_ +- Select **TCP** +- Click **Save** + + + diff --git a/docs/src/features/navdataserver/navionics.png b/docs/src/features/navdataserver/navionics.png new file mode 100644 index 000000000..2f53c5b2b Binary files /dev/null and b/docs/src/features/navdataserver/navionics.png differ diff --git a/docs/src/features/navdataserver/sk2nmea0183.png b/docs/src/features/navdataserver/sk2nmea0183.png new file mode 100644 index 000000000..fd1b60bce Binary files /dev/null and b/docs/src/features/navdataserver/sk2nmea0183.png differ diff --git a/docs/src/features/navdataserver/vesseldata.png b/docs/src/features/navdataserver/vesseldata.png new file mode 100644 index 000000000..a51347c66 Binary files /dev/null and b/docs/src/features/navdataserver/vesseldata.png differ diff --git a/img/calibration.png b/docs/src/img/calibration.png similarity index 100% rename from img/calibration.png rename to docs/src/img/calibration.png diff --git a/docs/src/img/course_provider.dia b/docs/src/img/course_provider.dia new file mode 100644 index 000000000..ad4ff9a0b Binary files /dev/null and b/docs/src/img/course_provider.dia differ diff --git a/docs/src/img/course_provider.svg b/docs/src/img/course_provider.svg new file mode 100644 index 000000000..75b84c7cf --- /dev/null +++ b/docs/src/img/course_provider.svg @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + Client + + + + + Course data + Provider plugin + + + + + + + + + + Course API + + + + + + + + + + + + + + + + + + + + + Course Data: + { + nextPoint: {...} + previousPoint: {...} + activeRoute: {...} + calcValues: { + ... + } + } + + + + + + + + + Deltas: Vessel speed, + heading, position, etc. + + + + diff --git a/docs/src/img/logo.png b/docs/src/img/logo.png new file mode 100644 index 000000000..9b91e3272 Binary files /dev/null and b/docs/src/img/logo.png differ diff --git a/docs/src/img/resource_provider.dia b/docs/src/img/resource_provider.dia new file mode 100644 index 000000000..c0b1b3cda Binary files /dev/null and b/docs/src/img/resource_provider.dia differ diff --git a/docs/src/img/resource_provider.svg b/docs/src/img/resource_provider.svg new file mode 100644 index 000000000..aa71e14aa --- /dev/null +++ b/docs/src/img/resource_provider.svg @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + Client + + + local + storage + + + + + route + wpt + + + + + + + + + + Resources API + + + + + + + + + + Provider + Plugins + + + + + chart + + + + + + wpt + + + + + + + + + + + + + + + on-line + service + + + + + + + + + + + + + + + + + + Provider I/F + + + + + + + + + + + + + diff --git a/docs/src/img/server_only.dia b/docs/src/img/server_only.dia new file mode 100644 index 000000000..0b857fd05 Binary files /dev/null and b/docs/src/img/server_only.dia differ diff --git a/docs/src/img/server_only.svg b/docs/src/img/server_only.svg new file mode 100644 index 000000000..9ed7490cb --- /dev/null +++ b/docs/src/img/server_only.svg @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Instrument + + + + + + + Instrument + + + + + + + + + Multi-Function + Display + + + + + + + + + + + + AIS + + + + + + GPS + + + + + + + + + + + + NMEA0183 + + + NMEA0183 + + + NMEA2000 + + + + + + + + + + Mobile + Device + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Cloud + + + + 3G or 4G + + + Wi-Fi + + + + + Signal K + Server + + + + + + Signal K + Sensor + + + + + + Generic + Sensor + + + + + + + + + + Generic + Sensor + + + + + + + + I2C + + + 1W + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Signal K JSON + over HTTP/WebSocket + + + + + + + + + diff --git a/docs/src/img/server_update.png b/docs/src/img/server_update.png new file mode 100644 index 000000000..a8b179c3a Binary files /dev/null and b/docs/src/img/server_update.png differ diff --git a/img/vesselpositions.png b/docs/src/img/vesselpositions.png similarity index 100% rename from img/vesselpositions.png rename to docs/src/img/vesselpositions.png diff --git a/docs/src/index.md b/docs/src/index.md new file mode 100644 index 000000000..8d68bd394 --- /dev/null +++ b/docs/src/index.md @@ -0,0 +1,20 @@ + + +## Introduction + +Signal K Server is software designed to be deployed on a vessel to act as a central hub which: +1. Collects data from devices and sensors on board +1. Aggregates and exposes it using the _[Signal K Data Standard](https://signalk.org/specification/latest/)_ +1. Exposes the collected data via REST APIs and websocket protocols over a standard WiFi, LAN or Internet connection. + +Through implementation of the _[Signal K Data Standard](https://signalk.org/specification/latest/)_, it enables data exchange between NMEA0183, NMEA2000 and other marine protocols facilitating two way communication between the various onboard systems. In addition it can also act as data hub for additional sensors ensuring their data appears within the single data model. _(Visit the [Signal K SensESP project](https://github.com/SignalK/SensESP) for [ESP32](https://en.wikipedia.org/wiki/ESP32) for details.)._ + +Data is made available to client applications / connections in JSON format making it widely accessible to Apps on phone / tablet devices and web applications. + + +Signal K Server is also extensible, providing a plugin framework which allows developers to create solutions that integrate and extend its capabilities. These solutions can be published to **npmjs** and installed via the **App Store** in the server's web-based user interface. + +![Server only setup](img/server_only.svg) + + + diff --git a/docs/src/installation/command_line.md b/docs/src/installation/command_line.md new file mode 100644 index 000000000..b3db4dce8 --- /dev/null +++ b/docs/src/installation/command_line.md @@ -0,0 +1,52 @@ +# Runtime Environment & Options + +Signal K Server provides the following command line options and environment variables to configure your implementation. + + + +## Command line options + +| Option | Description | +|--- |--- | +| `-s` | Override path to the settings file. _(same as `SIGNALK_NODE_SETTINGS` environment variable)_ | +| `-c` | Override the path to find server configuration. _(same as `SIGNALK_NODE_CONFIG_DIR` environment variable)_ | +| `--sample-nmea0183-data` | Starts signalk-server with sample NMEA0183 data. | +| `--sample-n2k-data` | Starts signalk-server with sample NMEA2000 data. | +| `--override-timestamps` | Override timestamps in the sample NMEA2000 data with current date and time. Doesn't apply nor makes a difference to NMEA0183 sample data. | +| `--securityenabled` | Enable security. For a fresh install this makes the Admin UI force the user to create an admin account before he/she can continue further into the UI. See [Security](../security.md#enabling-security) for further details. | + + + +## Environment variables + +| Variable | Description | +|--- |--- | +| `PORT` | Override the port for http/ws service (default is 3000). | +| `SSLPORT` | Override the port for https/wss service. If defined forces ssl as default protocol _(default port is 3443)_. | +| `PROTOCOL` | Override http/https where the server is accessed via https but the server sees http _(e.g. when Heroku handles https termination)_ | +| `EXTERNALPORT` | The port used in /signalk response and Bonjour advertisement. Has precedence over configuration file. | +| `EXTERNALHOST` | The host used in /signalk response and Bonjour advertisement. Has precedence over configuration file. | +| `FILEUPLOADSIZELIMIT` | Override the file upload size limit _(default is '10mb')_. | +| `NMEA0183PORT` | Override the port for the NMEA 0183 over tcp service _(default is 10110)_. | +| `TCPSTREAMPORT` | Override the port for the Signal K Streaming (deltas) over TCP. | +| `TCPSTREAMADDRESS` | Override the address the Signal K Stream (deltas) over TCP is listening on. | +| `DISABLEPLUGINS` | Disable all plugins so that they can not be enabled _(default is false)_. | +| `DEFAULTENABLEDPLUGINS` | A comma separated list of plugin ids that are overridden to be enabled by default if no setttings exist. Lower preference than `DISABLEPLUGINS`. | +| `PLUGINS_WITH_UPDATE_DISABLED` | A comma separated list of plugin that will not be updated. | +| `SECURITYSTRATEGY` | Override the security strategy module name. | +| `WSCOMPRESSION` | Compress websocket messages _(default is false)_. | +| `MAXSENDBUFFERSIZE` | The maximum number of bytes allowed in the server's send buffer of a WebSocket connection. The connection will be terminated if this is exceeded. Guards against slow or dysfunctional clients that can not cope with the message volume _(default is 512 * 1024 bytes)_. | +| `SIGNALK_SERVER_IS_UPDATABLE` | Allows the server to be updated through the GUI even if it is not installed in the standard paths _(default is false)_. If set to true, the server must have been installed with `npm install -g signalk-server`. | +| `SIGNALK_DISABLE_SERVER_UPDATES` | Disables server updates in the GUI _(default is false)_. | +| `DEBUG` | A comma-separated list of tags for debugging the specified module _(e.g signalk-server*,signalk-provider-tcp)_. Can now be defined directly in the graphical interface. More help on how to use the debug here: `https://www.npmjs.com/package/debug#wildcards` | +| `IS_IN_DOCKER` | Used to tell the server it is in Docker and not normally updateable _(default is false)_. | +| `NPMREGISTRYTIMEOUT` | How long to wait for the registry when retrieving the App Store listing _(default is 20s)_. | +| `SECRETKEY` | A secret string used to generate an authentication token _(the internal default autogenerated is a string of 512 hex chars like 'ef8307a4c7a4bd7...309d947bca3')_ | +| `ALLOW_DEVICE_ACCESS_REQUESTS` | Used when a device needs to gain access to a secured Signal K server _(default is true)_ (https://signalk.org/specification/1.4.0/doc/access_requests.html). | +| `ALLOW_NEW_USER_REGISTRATION` | _(default is true)_. | +| `ADMINUSER` | Force an account for admin user _(username:password format)_. | +| `PRESERIALCOMMAND` | Command to run before opening a serial port. | +| `SIGNALK_NODE_SETTINGS` | Override the path to the settings file. | +| `SIGNALK_NODE_CONFIG_DIR` | Override the path to find server configuration. Includes all run-time changing content: configuration files, plugins, plugin configuration files, webapps, and so forth. | + + diff --git a/docs/src/installation/install.md b/docs/src/installation/install.md new file mode 100644 index 000000000..ce93493da --- /dev/null +++ b/docs/src/installation/install.md @@ -0,0 +1,114 @@ +# Getting Started + +Signal K Server is a NodeJS application which can be installed on a variety of devices and operating systems. + +It is available for installation via: +1. NPM package +1. Docker image +1. GitHub repository + +See the relevant section below for instructions based on your target system. + + +### Prerequisites: + +_Signal K server requires NodeJs version >= 18 be installed on the target system prior to commencing installation._ + +--- + +## Raspberry Pi Installation + +[Install Signal K Server on Raspberry Pi](raspberry_pi_installation.md) outlines the process for getting Signal K Server up and running _(including supporting services)_ on Raspberry Pi OS. + +--- + +## Using Docker + +Signal K Server is available as a Docker image on _Docker Hub_ and _cr.signalk.io/signalk/signalk-server_. + +To simply run a Signal K Server with some sample data on a device with docker installed, enter the following into a terminal: +```shell +docker run --rm signalk/signalk-server --publish 3000:3000 +``` + +This will start an instance of Signal K Server on port 3000 which you can then access via the web based Admin UI by pointing your web browser at `http://localhost:3000`. + + +If you are wanting to use docker to deploy Signal K Server on your vessel you will need to: +1. Specify a location to persist the server's configuration so it is not lost between restarts +1. Run the instance as a background process + +_Example: Run as background process and store server configuration in the current folder:_ +```shell +docker run -d --init --name signalk-server -p 3000:3000 -v $(pwd):/home/node/.signalk signalk/signalk-server +``` + +You are ready to now **[configure](../setup/configuration.md)** your installation and connect data from devices on your boat. + +--- + + +## Installation via NPM + +Signal K Server can be installed directly using NPM. + +_Windows:_ + +- See [Installing on Windows](#installing-on-windows) below. + +_Linux / macOS:_ +```shell +sudo npm install -g signalk-server +``` + +Once installation is complete, enter the following in a terminal window, to generate a settings file and configure the server to start automatically: +```shell +sudo signalk-server-setup +``` + +If you choose not to use `signalk-server-setup` you can start the server by entering the following in a terminal window: +```shell +signalk-server +``` + +--- + + +## Installing on Windows + +Please use the [Signal K installer for Windows](https://github.com/SignalK/signalk-server-windows) to install Signal K Server on a Windows device. + +--- + +## Install using Git + +Installation from the GitHub repository is useful when developing plugins and components. + +To do this enter the following commands in a terminal window: +```shell +# Copy the files to your device +git clone https://github.com/SignalK/signalk-server.git + +# change to the folder containing the downloaded files +cd signalk-server + +# install the dependencies +npm install + +# build all packages in the repository +npm run build:all +``` + +To start Signal K Server with a sample configuration file and some sample data, enter the following into a terminal: +_To use NMEA0183 sample data:_ +``` shell +bin/nmea-from-file +``` + +_To use NMEA2000 sample data:_ +``` shell +bin/n2k-from-file +``` +The server will start playing back data from the specified sample file that can be viewed using the _Data Browser_ in the Admin UI _(`http://localhost:3000`)_ or via REST API / websocket connection. + +--- diff --git a/docs/src/installation/raspberry_pi_installation.md b/docs/src/installation/raspberry_pi_installation.md new file mode 100644 index 000000000..e6c2a8c8a --- /dev/null +++ b/docs/src/installation/raspberry_pi_installation.md @@ -0,0 +1,137 @@ +# Installation on Raspberry Pi + + +Installation of Signal K server can consists of the following steps: + +1. Install the tools and libraries required to run the Signal K server (the dependencies) +1. Install a Signal K Server to process the Signal K data +1. Run the Setup script. + +_**Important:** If you are updating a Signal K server installation, especially if upgrading an installed version <= 1.40.0, [please check here first](./updating.md)._ + +### Prerequisites: + +Raspberry Pi OS is installed on the device. + +For instructions on how to install the operating system [can be found here.](https://www.raspberrypi.org/documentation/computers/getting-started.html#setting-up-your-raspberry-pi). + +_Note: It is also possible to perform a "headless install" using `Raspberry Pi OS Lite` since the GUI for Signal K is browser based._ + + +Once the OS installation has been completed, you are ready to commence. + +--- + +## Install the Dependencies + +1. Log in to the RPi Desktop and open a terminal. + +1. Update the list of install packages. + ``` + $ sudo apt update + ``` + +1. Install NodeJS and npm. + ``` + $ curl -sL https://deb.nodesource.com/setup_18.x | sudo -E bash - + $ sudo apt-get install -y nodejs + ``` + +1. Ensure that we're using the latest version of npm. + ``` + $ sudo npm install -g npm@latest + ``` + + Use the following command to check the versions of NodeJS and npm installed. + + ``` + node -v && npm -v + ``` + Ensure the reported versions are equal to or greater than `v18.15.0, 9.5.0` respectively. + +1. Install a Bonjour (mDNS) service for Linux called Avahi, which allows Apps and other network devices to Discover the Signal K server. + ``` + $ sudo apt install libnss-mdns avahi-utils libavahi-compat-libdnssd-dev + ``` + +## Install Signal K Server + +``` +$ sudo npm install -g signalk-server +``` + +You can test that installation was successful by starting the server using some +sample data. + +``` +$ signalk-server --sample-nmea0183-data +``` + +You should see the terminal output "signalk-server running at 0.0.0.0:3000" as shown below... +``` +$ signalk-server --sample-nmea0183-data +Using sample data from /usr/lib/node_modules/signalk-server/samples/plaka.log +signalk-server running at 0.0.0.0:3000 +``` + +The Signal K Node Server is now reading and publishing sample NMEA0183 data from the specified file. + +Using a Web browser enter the following URL: `http://127.0.0.1:3000/signalk` which should display the following information indicating the server is up and running. + +```JSON +{ + "endpoints":{ + "v1":{ + "version":"2.0.0", + "signalk-http":"http://127.0.0.1:3000/signalk/v1/api/", + "signalk-ws":"ws://127.0.0.1:3000/signalk/v1/stream", + "signalk-tcp":"tcp://127.0.0.1:3858" + } + }, + "server":{ + "id":"signalk-server-node", + "version":"2.0.0" + } +} +``` + +## Run the Setup Script + +Now that you have Signal K server installed, you will want to generate a settings file for your vessel +and configure your RPi to start the server automatically. To do this run the setup script by entering the following command and follow the prompts. + +``` +$ sudo signalk-server-setup +``` + +You can re-run this command at any time in the future to change the settings. + +_Note: The setup script will enable security which will require you to `Login`` from the Admin UI. +Clicking `Login` for the first time will prompt you to create a user and password._ + +Signal K server will now be started automatically when your RPi boots up. + + +If you want to temporarily stop the Signal K server, you can do so by entering the following commands: +``` +$ sudo systemctl stop signalk.service +$ sudo systemctl stop signalk.socket +``` + +To start Signal K server again enter the following commands: +``` +$ sudo systemctl start signalk.service +$ sudo systemctl start signalk.socket +``` + +To stop Signal K server from starting automatically enter the following commands: +``` +$ sudo systemctl disable signalk.service +$ sudo systemctl disable signalk.socket +``` + + +You are ready to now **[configure](../setup/configuration.md)** your installation and connect data from devices on your boat. + + + diff --git a/docs/src/installation/updating.md b/docs/src/installation/updating.md new file mode 100644 index 000000000..1cc8fc8fb --- /dev/null +++ b/docs/src/installation/updating.md @@ -0,0 +1,87 @@ +# Updating your Installation + +Signal K Server is frequently updated to introduce new features and fix issues that have been reported. +Sometime these updates require that NodeJS or other supporting software on your device to be upgraded to support the new functionality. + +Additionally your device's operating system are constantly evolving to address security issues as well as providing new capabilities. + +Regularly updating your installation will reduce both the volume of data download and the time taken to complete the process. Connecting your device to a network with good broadband speed before performing an update is recommended. + + +Updates fall into four categories: + +1. Device Operating system (e.g. RaspberryPi OS) +1. NodeJS / NPM +1. Signal K Server +1. Signal K WebApps and Plugins + + +## Update Device Operating System + +Instructions will vary depending on your device but for linux based systems such as the Raspberry Pi the following instrctions are used to update the OS. + +From a terminal window enter the following commands: +```shell +sudo apt update + +sudo apt dist-upgrade +``` + +If you have not performed an update for a while these commands may take a while to complete, just be patient and make sure everything completes correctly. + +After the process has completed `restart` your device. + + + +## Update NodeJS and NPM + +To ensure the version of NodeJS on your device is supported by Signal K Server _[(see prerequisites)](install.md#prerequisites)_, check the installed version by +entering the following in a terminal window: +```bash +node -v + +# example response +v18.17.0 +``` + +If the version of NodeJS displayed is lower than the version supported by Signal K Server then you can update it with the following command: +```shell +sudo apt upgrade nodejs +``` + +It is also recommended to update the version of the Node Package Manager (NPM). +```shell +sudo npm install -g npm@latest +``` + + +## Update Signal K Server + +When an update is available for Signal K Server a visual indication is displayed in the Admin UI. + +**Important!** +Before updating please ensure the version of NodeJS on your device is [supported by Signal K Server](install.md#prerequisites). + +_**If you are updating from Signal K Server version v1.40.0 or earlier please [read this first](https://github.com/SignalK/signalk-server/wiki/Updating-to-Node.js-18) before proceeding.**_ + +Click on _Server -> Update_ to display information about the new version. + +![server_update](../img/server_update.png) + +Click **Update** to start the installation. + +After the installation is complete, click **Restart** to launch the updated Signal K Server. + + +### WebApps and Plugins + +After updating Signal K Server some plugins and WebApps may also need to be updated. + +The AppStore is where WebApps and Plugins can be installed, removed or updated. +Those with an update available will be listed in _Appstore -> Updates_ in the Admin UI. + +Clicking on the _download cloud_ button next to the WebApp / Plugin you wish to update. + +After all installations have been completed, click **Restart** to activate the updated WebApps and Plugins. + + diff --git a/docs/src/security.md b/docs/src/security.md new file mode 100644 index 000000000..9e5e95fe4 --- /dev/null +++ b/docs/src/security.md @@ -0,0 +1,118 @@ +# Security + +## Introduction + + +The umbrella term _Security_ in Signal K server refers to the difference between running a server, that any one connected to the network can access and alter at will **(unsecured)** , and one with restrictions in place **(secured)**. + +The available security options relate to: +* **authentication**: Users and / or connecting devices having to provide a credential to gain access to the server _(e.g. username & password, access token, etc.)_. +* **access control**: Based on the authentication, access is granted to only specific Signal K data and server configuration. +* **communications**: Network traffic is encrypted and the identity of the server verified to protect against eavesdropping. +* **network services**: Control which of the server's services/interfaces are configured and active _(e.g. does it allow unsecured read/write over the network)_. + + +## Enabling Security + +When Signal K Server does not have security enabled, the `Login` option at the top right corner of the Admin UI will not be available. + +Security can be enabled in several ways: +1. Using the Admin UI, select _Security -> Users_ and then: + - Click **Add** + - Enter a **user id** + - Enter a **password** and confirm it + - In **Permissions** select **Admin** + - Click **Apply**. + - Restart the Signal K Server. + +2. Starting the server with the `--securityenabled` command line option +3. Adding the following section in the settings file + +```JSON +"security": { + "strategy": "./tokensecurity", + } +``` + +When security is enabled, the next time you access the Admin UI it will prompt you to create an administrator account. + +Security configuration is stored in file called `security.json` which will be located in the server configuration directory. + + +## Disabling Security / Lost Admin Credentials + +In case the administrator user credentials are lost, removing the `security.json` file and restarting the server will restore access to the Admin UI. + + +## Access Control + +Access control lists _(acls)_ allow for fine grained access to specific data in Signal K. They specify the permissions assigned to users for resources within specifc contexts and are defined within the `security.json` file. + +The following example defines acls for the self context allowing: +1. Anyone to read the paths `"steering.*"`, `"navigation.*"`, `"name"`, `"design.aisShipType"` and grants the admin user permission to write (update) those paths. + +2. The user _john_ to read any data coming from the `actisense.35` $source. + +3. For all other paths, only the admin user to read and no one can write. + +```JSON + "acls": [ + { + "context": "vessels.self", + "resources": [ + { + "paths": ["steering.*", "navigation.*", "name", "design.aisShipType"], + "permissions": [ + { + "subject": "any", + "permission": "read" + }, + { + "subject": "admin", + "permission": "write" + } + ] + }, + { + "sources": [ "actisense.35" ], + "permissions": [ + { + "subject": "john", + "permission": "read" + } + ] + }, + { + "paths": ["*"], + "permissions": [ + { + "subject": "admin", + "permission": "read" + } + ] + } + ] + } + ] + ``` + + _Note: If there is no match is found for a specific path in the acl list, then permission will be denied to that path!_ + +## Active network services + +Signal K Server's main network services are: +- The _primary Signal K http / WebSocket interface_, with options to use TLS encryption and authentication _(read/write)_ +- *NMEA0183 data over TCP* on port 10110 _(read only)_ +- *Signal K over TCP* on port 8375 _(read/write)_ + +In addition the user may configure any number of TCP, UDP and Websocket connections, some of which allow write access to the server. + +The security implication of these connections is that with no security options turned on _devices connected to the network will have both read and write access to practically all of its data and settings_. + +People often dismiss local network access by saying that their boat's local network is secure enough. But one very common scenario is connecting your Signal K server _(e.g. a Raspberry Pi)_ to a marina wifi. +Many wifi networks allow communication between all connected computers, so your Signal K server will be advertising its services over MDNS to all other connected devices. + +So in the case that your server has a manually configured connection for _NMEA0183 over UDP_, NMEA0183 data broadcast by other devices will be received and written into your SIgnal K data. + +NMEA0183 connections over TCP and UDP are inherently unsafe. There are no options for authentication and / or secure communication. In comparison Signal K over TLS and HTTP / WebSockets can provide secure, authenticated read and write access to your data. + diff --git a/docs/src/setup/configuration.md b/docs/src/setup/configuration.md new file mode 100644 index 000000000..2116a5e22 --- /dev/null +++ b/docs/src/setup/configuration.md @@ -0,0 +1,94 @@ +## Configuring Signal K Server + +Signal K Server provides an Admin UI to allow you to easily configure your installation. + +Open the Admin UI using a web browser on the device where Signal K server is installed (if the defaults have not been changed) by navigating to `http://localhost:3000`. + +### Create an Admin account + +It is considered good practise to enable security and create an administrator account to controll access to your server and protect your data. + +If you ran the `signalk-server-setup` script, security will be enabled and you will be presented with a login screen when accessing the Admin UI. + +If the login screen is not displayed, click `Login` _(top right of screen)_ to display the prompt to create a user and password. + +Alternatively, from the menu select _Security -> Users_ and then: + +1. Click **Add** +1. Enter a **user id** +1. Enter a **password** and confirm it +1. In **Permissions** select **Admin** +1. Click **Apply**. + + +After creating the account, the server needs to be restarted. + +How you restart the server will depend on the installation type _(i.e. installed from NPM, embedded on a commercial device, etc)_. Power cycling the device that Signal K Server is always an option. + +### Set up data connections + +To get data into Signal K server you will need to configure one or more data connections via the _Server -> Data Connections_ menu option. + +From this screen you can add connections for various data types including: +- NMEA2000 +- NMEA0183 +- Signal K +- SeaTalk +- File Stream + +The options presented will vary based on the data type chosen. + +**_NMEA2000_**: The processing of NMEA2000 PGNs is done by [n2k-signalk](https://github.com/SignalK/n2k-signalk) via [canboatjs](https://github.com/canboat/canboatjs). + +Please refer to the [Canboat PGN database](https://canboat.github.io/canboat/canboat.html) to see what PGNs are supported. + + +**_NMEA0183_**: The processing of NMEA0183 sentences is done by [nmea0183-signalk](https://github.com/SignalK/signalk-parser-nmea0183) + + +### Install Plugins and Webapps + +Signal K server functionality can be extended through the use of plugins and webapps. + +Plugins typically extend data acquisition, data processing or enable operations (i.e. protocol conversion, etc). + +Webapps provide a user interface to view / interact with data or perform operations enabling full featured solutions such as a Chartplotter. + +To install, update or remove plugins and webapps select _Appstore_ from the menu. + +Select: + +- `Installed` to view a list of plugins and webapps currently installed. + +- `Updates` to view a list of plugins and webapps that have updates available. + +- `Available` to view a list of available plugins and webapps that can be filtered by categry. + +The entries displayed with a blue icon are webapps, those with a green icon are plugins and those with both blue and green icons are plugins with a webapp providing a user interface. + +_Note: An internet connection is required for Signal K Server to list, install and update AppStore listings._ + +To install, click the `download` icon on the right hand side of the entry. + +To view a list of Plugins and Webapps directly from the NPM registry select the links below. + + * [Plugins](https://www.npmjs.com/search?q=keywords%3Asignalk-node-server-plugin) + * [Webapps](https://www.npmjs.com/search?q=keywords:signalk-webapp) + +**_Note: A restart of the Signal K server is required after plugins or webapps have been installed or updated._** + +Click the _Restart_ button at the top right of the screen to restart the server. + +After the server has restarted, the installed plugin(s) can be configued by selecting _Server -> Plugin Config_ menu entry. + + +### Trouble shooting and the Server Log + +If things are not working as expected after installing a plugin or webapp, select _Server -> Server Log_ to view the server's log. If the errors logged there are not providing the information required, you can enable debugging for individual components and plugins by toggling the switch to activate them. + +Enabling the `Remember debug setting` ensure your selections are remebered after a server restart. + +## Add your Logo + +You can change the logo image displayed at the top left of the Admin UI screen. +To do this, add an SVG file named `logo.svg` in the settings directory _(default: $HOME/.signalk/)_. diff --git a/docs/src/setup/generating_tokens.md b/docs/src/setup/generating_tokens.md new file mode 100644 index 000000000..317683960 --- /dev/null +++ b/docs/src/setup/generating_tokens.md @@ -0,0 +1,38 @@ +# Generating Tokens + +## Overview + +For a device to be able to interact with a Signal K server with security enabled, it is require to pass an access token with each request. + +_Examples include display / gauge, temperature sensor or client with no user interface._ + +To get an access token the following methods can be used: +1. The device can submit an [Access Request](https://signalk.org/specification/1.5.0/doc/access_requests.html) which needs to be actioned via the Signal K Server UI. +2. Generate a token against a user account that has been configured on the Signal K Server. + + +### Generate Token + +To generate a token against a user account that has been configured on the Signal K Server use the `signalk-generate-token` utility. + +The `signalk-generate-token` utility is run from a terminal session on the Signal K Server and accepts the following parameters: +- `-u `: The user account against which the token is created. +- `-e