UNPKG

signalk-server

Version:

An implementation of a [Signal K](http://signalk.org) server for boats.

1 lines 1.08 MB
{"doc_urls":["index.html#introduction","installation/install.html#getting-started","installation/install.html#prerequisites","installation/install.html#raspberry-pi-installation","installation/install.html#using-docker","installation/install.html#installation-via-npm","installation/install.html#installing-on-windows","installation/install.html#install-using-git","installation/raspberry_pi_installation.html#installation-on-raspberry-pi","installation/raspberry_pi_installation.html#prerequisites","installation/raspberry_pi_installation.html#install-the-dependencies","installation/raspberry_pi_installation.html#install-signal-k-server","installation/raspberry_pi_installation.html#run-the-setup-script","installation/updating.html#updating-your-installation","installation/updating.html#update-device-operating-system","installation/updating.html#update-nodejs-and-npm","installation/updating.html#update-signal-k-server","installation/updating.html#webapps-and-plugins","installation/command_line.html#runtime-environment--options","installation/command_line.html#command-line-options","installation/command_line.html#environment-variables","security.html#security","security.html#introduction","security.html#enabling-security","security.html#disabling-security--lost-admin-credentials","security.html#access-control","security.html#active-network-services","setup/generating_tokens.html#generating-tokens","setup/generating_tokens.html#overview","setup/generating_tokens.html#generate-token","setup/generating_tokens.html#access-requests","setup/configuration.html#configuring-signal-k-server","setup/configuration.html#create-an-admin-account","setup/configuration.html#set-up-data-connections","setup/configuration.html#install-plugins-and-webapps","setup/configuration.html#trouble-shooting-and-the-server-log","setup/configuration.html#landing-page","setup/configuration.html#add-your-logo","setup/seatalk/seatalk.html#seatalk-connections","setup/seatalk/seatalk.html#introduction","setup/seatalk/seatalk.html#hardware","setup/seatalk/seatalk.html#software","setup/seatalk/seatalk.html#data-connection","setup/seatalk/seatalk.html#viewing-received-data","features/anchoralarm/anchoralarm.html#feature-anchor-alarm","features/anchoralarm/anchoralarm.html#contents","features/anchoralarm/anchoralarm.html#introduction","features/anchoralarm/anchoralarm.html#prerequisites","features/anchoralarm/anchoralarm.html#configuration","features/anchoralarm/anchoralarm.html#using-the-anchor-alarm","features/anchoralarm/anchoralarm.html#alarms--notifications","features/anchoralarm/anchoralarm.html#audible-alarms-using-the-raspberry-pi-audio-connector","features/anchoralarm/anchoralarm.html#alarm-to-nmea2000-network","features/anchoralarm/anchoralarm.html#remote-notifications","features/anchoralarm/anchoralarm.html#switching-a-relay","features/anchoralarm/anchoralarm.html#alternatives-to-wilhelmsk","features/anchoralarm/anchoralarm.html#anchor-alarm-plugin","features/anchoralarm/anchoralarm.html#freeboard-sk","features/navdataserver/navdataserver.html#signal-k-server-as-a-nmea0183-data-server","features/navdataserver/navdataserver.html#introduction","features/navdataserver/navdataserver.html#prerequisites","features/navdataserver/navdataserver.html#server-setup-and-configuration","features/navdataserver/navdataserver.html#configuring-apps","features/datalogging/datalogging.html#data-logging","support/help.html#help--support","support/faq.html#frequently-asked-questions","support/sponsor.html#sponsor-signal-k","develop/developer_notes.html#notes-for-developers","develop/developer_notes.html#looking-ahead","develop/developer_notes.html#stream-interface","develop/developer_notes.html#offline-use","develop/developer_notes.html#deprecations-and-breaking-changes","whats_new.html#whats-new-in-version-2","whats_new.html#course-api","whats_new.html#resources-api","whats_new.html#notes-for-developers","whats_new.html#nmea0183--nmea2000-message-processing","whats_new.html#stream-updates","breaking_changes.html#changes--deprecations","breaking_changes.html#changes","breaking_changes.html#1-resource-id-prefix-assignment","breaking_changes.html#2-resource-attributes","breaking_changes.html#deprecations","breaking_changes.html#1-coursegreatcircle-courserhumbline-paths","develop/webapps.html#webapps-and-components","develop/webapps.html#introduction","develop/webapps.html#webapp-structure","develop/webapps.html#application-data-storing-webapp-data-on-the-server","develop/webapps.html#discovering-server-features","develop/webapps.html#embedded-components-and-admin-ui--server-interfaces","develop/webapps.html#webapp--component-and-admin-ui--server-interfaces","develop/webapps.html#authentication-and-session-management","develop/plugins/server_plugin.html#server-plugins","develop/plugins/server_plugin.html#overview","develop/plugins/server_plugin.html#getting-started-with-plugin-development","develop/plugins/server_plugin.html#prerequisites","develop/plugins/server_plugin.html#setting-up-your-project","develop/plugins/server_plugin.html#link-your-project-to-signal-k-server","develop/plugins/server_plugin.html#debugging","develop/plugins/server_plugin.html#start-coding","develop/plugins/server_plugin.html#javascript","develop/plugins/server_plugin.html#typescript","develop/plugins/server_plugin.html#plugin-configuration--schema","develop/plugins/server_plugin.html#ui-schema","develop/plugins/server_plugin.html#making-a-plugin-enabled-by-default","develop/plugins/server_plugin.html#add-an-openapi-definition","develop/plugins/server_plugin.html#logging","develop/plugins/server_plugin.html#removing-a-plugin","develop/plugins/server_plugin.html#examples","develop/plugins/deltas.html#processing-data-from-the-server","develop/plugins/deltas.html#reading-the-current-path-value","develop/plugins/deltas.html#subscribing-to-deltas","develop/plugins/deltas.html#sending-deltas","develop/plugins/deltas.html#sending-nmea-2000-data-from-a-plugin","develop/plugins/deltas.html#sending-a-message-on-nmea2000-startup","develop/plugins/server_plugin_api.html#server-api-for-plugins","develop/plugins/server_plugin_api.html#discover-features","develop/plugins/server_plugin_api.html#accessing-the-data-model","develop/plugins/server_plugin_api.html#working-with-deltas","develop/plugins/server_plugin_api.html#configuration","develop/plugins/server_plugin_api.html#messages-and-debugging","develop/plugins/server_plugin_api.html#serial-port","develop/plugins/server_plugin_api.html#resources-api-interface","develop/plugins/server_plugin_api.html#course-api-interface","develop/plugins/server_plugin_api.html#propertyvalues","develop/plugins/server_plugin_api.html#exposing-custom-http-paths--openapi","develop/plugins/server_plugin_api.html#plugin-configuration-http-api","develop/plugins/resource_provider_plugins.html#resource-provider-plugins","develop/plugins/resource_provider_plugins.html#overview","develop/plugins/resource_provider_plugins.html#resources-api","develop/plugins/resource_provider_plugins.html#provider-plugins","develop/plugins/resource_provider_plugins.html#provider-methods","develop/plugins/resource_provider_plugins.html#registering-as-a-resource-provider","develop/plugins/resource_provider_plugins.html#methods","develop/rest-api/course_calculations.html#course-calculations-and-providers","develop/rest-api/course_calculations.html#calculated-value-paths-calcvalues","develop/rest-api/course_calculations.html#course-notifications","develop/rest-api/course_calculations.html#course-provider-plugins","develop/plugins/autopilot_provider_plugins.html#autopilot-provider-plugins","develop/plugins/autopilot_provider_plugins.html#overview","develop/plugins/autopilot_provider_plugins.html#provider-plugins","develop/plugins/autopilot_provider_plugins.html#registering-as-an-autopilot-provider","develop/plugins/autopilot_provider_plugins.html#sending-updates-and-notifications-from-autopilot-device","develop/plugins/autopilot_provider_plugins.html#provider-methods","develop/plugins/autopilot_provider_plugins.html#unhandled-operations","develop/plugins/publishing.html#publishing-to-the-appstore","develop/rest-api/open_api.html#rest-apis","develop/rest-api/open_api.html#apis-available-in-signal-k-server-v200-and-later","develop/rest-api/course_api.html#working-with-the-course-api","develop/rest-api/course_api.html#overview","develop/rest-api/course_api.html#setting-a-course","develop/rest-api/course_api.html#1-navigate-to-a-location","develop/rest-api/course_api.html#2-navigate-to-a-waypoint","develop/rest-api/course_api.html#3-follow-a-route","develop/rest-api/course_api.html#retrieving-course-information","develop/rest-api/course_api.html#cancelling-navigation","develop/rest-api/course_api.html#configuration","develop/rest-api/course_api.html#course-calculations","develop/rest-api/course_calculations.html#course-calculations-and-providers","develop/rest-api/course_calculations.html#calculated-value-paths-calcvalues","develop/rest-api/course_calculations.html#course-notifications","develop/rest-api/course_calculations.html#course-provider-plugins","develop/rest-api/resources_api.html#working-with-the-resources-api","develop/rest-api/resources_api.html#overview","develop/rest-api/resources_api.html#retrieving-resources","develop/rest-api/resources_api.html#deleting-resources","develop/rest-api/resources_api.html#creating--updating-resources","develop/rest-api/resources_api.html#multiple-providers-for-a-resource-type","develop/rest-api/notifications_api.html#notifications-api","develop/rest-api/autopilot_api.html#working-with-the-autopilot-api","develop/rest-api/autopilot_api.html#overview","develop/rest-api/autopilot_api.html#common-operations","develop/rest-api/autopilot_api.html#the--default--autopilot","develop/rest-api/autopilot_api.html#getting-the-default-autopilot-identifier","develop/rest-api/autopilot_api.html#setting-an-autopilot-as-the-default","develop/rest-api/autopilot_api.html#listing-the-available-autopilots","develop/rest-api/autopilot_api.html#autopilot-deltas","develop/rest-api/autopilot_api.html#autopilot-notifications","develop/rest-api/autopilot_api.html#autopilot-offline--unreachable","develop/rest-api/autopilot_api.html#autopilot-operations","develop/rest-api/autopilot_api.html#retrieving-autopilot-status","develop/rest-api/autopilot_api.html#setting-the-autopilot-state","develop/rest-api/autopilot_api.html#getting-the-autopilot-state","develop/rest-api/autopilot_api.html#setting-the-autopilot-mode","develop/rest-api/autopilot_api.html#getting-the-autopilot-mode","develop/rest-api/autopilot_api.html#setting-the-target-value","develop/rest-api/autopilot_api.html#getting-the-current-target-value","develop/rest-api/autopilot_api.html#engaging--disengaging-the-autopilot","develop/rest-api/autopilot_api.html#perform-a-tack","develop/rest-api/autopilot_api.html#perform-a-gybe","develop/rest-api/autopilot_api.html#dodging-obstacles","develop/rest-api/anchor_api.html#anchor-api","develop/contributing.html#contributing","develop/contributing.html#submitting-a-pull-request-pr"],"index":{"documentStore":{"docInfo":{"0":{"body":123,"breadcrumbs":2,"title":1},"1":{"body":27,"breadcrumbs":3,"title":2},"10":{"body":84,"breadcrumbs":6,"title":2},"100":{"body":35,"breadcrumbs":2,"title":1},"101":{"body":229,"breadcrumbs":2,"title":1},"102":{"body":113,"breadcrumbs":4,"title":3},"103":{"body":36,"breadcrumbs":3,"title":2},"104":{"body":20,"breadcrumbs":5,"title":4},"105":{"body":64,"breadcrumbs":4,"title":3},"106":{"body":14,"breadcrumbs":2,"title":1},"107":{"body":38,"breadcrumbs":3,"title":2},"108":{"body":14,"breadcrumbs":2,"title":1},"109":{"body":141,"breadcrumbs":6,"title":3},"11":{"body":82,"breadcrumbs":8,"title":4},"110":{"body":42,"breadcrumbs":7,"title":4},"111":{"body":96,"breadcrumbs":5,"title":2},"112":{"body":39,"breadcrumbs":5,"title":2},"113":{"body":54,"breadcrumbs":8,"title":5},"114":{"body":29,"breadcrumbs":7,"title":4},"115":{"body":30,"breadcrumbs":6,"title":3},"116":{"body":72,"breadcrumbs":5,"title":2},"117":{"body":200,"breadcrumbs":6,"title":3},"118":{"body":379,"breadcrumbs":5,"title":2},"119":{"body":44,"breadcrumbs":4,"title":1},"12":{"body":118,"breadcrumbs":7,"title":3},"120":{"body":171,"breadcrumbs":5,"title":2},"121":{"body":13,"breadcrumbs":5,"title":2},"122":{"body":324,"breadcrumbs":6,"title":3},"123":{"body":73,"breadcrumbs":6,"title":3},"124":{"body":191,"breadcrumbs":4,"title":1},"125":{"body":85,"breadcrumbs":8,"title":5},"126":{"body":31,"breadcrumbs":7,"title":4},"127":{"body":0,"breadcrumbs":6,"title":3},"128":{"body":78,"breadcrumbs":4,"title":1},"129":{"body":73,"breadcrumbs":5,"title":2},"13":{"body":76,"breadcrumbs":5,"title":2},"130":{"body":136,"breadcrumbs":5,"title":2},"131":{"body":381,"breadcrumbs":5,"title":2},"132":{"body":121,"breadcrumbs":6,"title":3},"133":{"body":83,"breadcrumbs":4,"title":1},"134":{"body":38,"breadcrumbs":6,"title":3},"135":{"body":58,"breadcrumbs":7,"title":4},"136":{"body":20,"breadcrumbs":5,"title":2},"137":{"body":83,"breadcrumbs":6,"title":3},"138":{"body":17,"breadcrumbs":6,"title":3},"139":{"body":85,"breadcrumbs":4,"title":1},"14":{"body":42,"breadcrumbs":7,"title":4},"140":{"body":95,"breadcrumbs":5,"title":2},"141":{"body":100,"breadcrumbs":6,"title":3},"142":{"body":117,"breadcrumbs":8,"title":5},"143":{"body":529,"breadcrumbs":5,"title":2},"144":{"body":31,"breadcrumbs":5,"title":2},"145":{"body":194,"breadcrumbs":4,"title":2},"146":{"body":29,"breadcrumbs":4,"title":2},"147":{"body":67,"breadcrumbs":9,"title":7},"148":{"body":0,"breadcrumbs":7,"title":3},"149":{"body":84,"breadcrumbs":5,"title":1},"15":{"body":50,"breadcrumbs":6,"title":3},"150":{"body":18,"breadcrumbs":6,"title":2},"151":{"body":20,"breadcrumbs":7,"title":3},"152":{"body":23,"breadcrumbs":7,"title":3},"153":{"body":212,"breadcrumbs":7,"title":3},"154":{"body":176,"breadcrumbs":7,"title":3},"155":{"body":37,"breadcrumbs":6,"title":2},"156":{"body":205,"breadcrumbs":5,"title":1},"157":{"body":17,"breadcrumbs":6,"title":2},"158":{"body":38,"breadcrumbs":9,"title":3},"159":{"body":58,"breadcrumbs":10,"title":4},"16":{"body":55,"breadcrumbs":7,"title":4},"160":{"body":20,"breadcrumbs":8,"title":2},"161":{"body":83,"breadcrumbs":9,"title":3},"162":{"body":0,"breadcrumbs":7,"title":3},"163":{"body":77,"breadcrumbs":5,"title":1},"164":{"body":127,"breadcrumbs":6,"title":2},"165":{"body":29,"breadcrumbs":6,"title":2},"166":{"body":124,"breadcrumbs":7,"title":3},"167":{"body":281,"breadcrumbs":8,"title":4},"168":{"body":65,"breadcrumbs":6,"title":2},"169":{"body":0,"breadcrumbs":7,"title":3},"17":{"body":39,"breadcrumbs":5,"title":2},"170":{"body":54,"breadcrumbs":5,"title":1},"171":{"body":17,"breadcrumbs":6,"title":2},"172":{"body":58,"breadcrumbs":6,"title":2},"173":{"body":13,"breadcrumbs":8,"title":4},"174":{"body":27,"breadcrumbs":7,"title":3},"175":{"body":46,"breadcrumbs":7,"title":3},"176":{"body":35,"breadcrumbs":6,"title":2},"177":{"body":51,"breadcrumbs":6,"title":2},"178":{"body":11,"breadcrumbs":7,"title":3},"179":{"body":21,"breadcrumbs":6,"title":2},"18":{"body":12,"breadcrumbs":7,"title":3},"180":{"body":59,"breadcrumbs":7,"title":3},"181":{"body":19,"breadcrumbs":7,"title":3},"182":{"body":13,"breadcrumbs":7,"title":3},"183":{"body":19,"breadcrumbs":7,"title":3},"184":{"body":13,"breadcrumbs":7,"title":3},"185":{"body":83,"breadcrumbs":7,"title":3},"186":{"body":16,"breadcrumbs":8,"title":4},"187":{"body":60,"breadcrumbs":7,"title":3},"188":{"body":27,"breadcrumbs":6,"title":2},"189":{"body":27,"breadcrumbs":6,"title":2},"19":{"body":78,"breadcrumbs":7,"title":3},"190":{"body":56,"breadcrumbs":6,"title":2},"191":{"body":72,"breadcrumbs":6,"title":2},"192":{"body":27,"breadcrumbs":2,"title":1},"193":{"body":263,"breadcrumbs":5,"title":4},"2":{"body":13,"breadcrumbs":2,"title":1},"20":{"body":326,"breadcrumbs":6,"title":2},"21":{"body":0,"breadcrumbs":2,"title":1},"22":{"body":76,"breadcrumbs":2,"title":1},"23":{"body":75,"breadcrumbs":3,"title":2},"24":{"body":14,"breadcrumbs":6,"title":5},"25":{"body":99,"breadcrumbs":3,"title":2},"26":{"body":143,"breadcrumbs":4,"title":3},"27":{"body":0,"breadcrumbs":5,"title":2},"28":{"body":47,"breadcrumbs":4,"title":1},"29":{"body":101,"breadcrumbs":5,"title":2},"3":{"body":20,"breadcrumbs":4,"title":3},"30":{"body":8,"breadcrumbs":5,"title":2},"31":{"body":25,"breadcrumbs":5,"title":4},"32":{"body":82,"breadcrumbs":4,"title":3},"33":{"body":275,"breadcrumbs":5,"title":4},"34":{"body":148,"breadcrumbs":4,"title":3},"35":{"body":35,"breadcrumbs":5,"title":4},"36":{"body":24,"breadcrumbs":3,"title":2},"37":{"body":18,"breadcrumbs":3,"title":2},"38":{"body":0,"breadcrumbs":5,"title":2},"39":{"body":49,"breadcrumbs":4,"title":1},"4":{"body":98,"breadcrumbs":3,"title":2},"40":{"body":83,"breadcrumbs":4,"title":1},"41":{"body":209,"breadcrumbs":4,"title":1},"42":{"body":101,"breadcrumbs":5,"title":2},"43":{"body":60,"breadcrumbs":6,"title":3},"44":{"body":0,"breadcrumbs":5,"title":3},"45":{"body":9,"breadcrumbs":3,"title":1},"46":{"body":69,"breadcrumbs":3,"title":1},"47":{"body":23,"breadcrumbs":3,"title":1},"48":{"body":493,"breadcrumbs":3,"title":1},"49":{"body":138,"breadcrumbs":5,"title":3},"5":{"body":51,"breadcrumbs":4,"title":3},"50":{"body":20,"breadcrumbs":4,"title":2},"51":{"body":19,"breadcrumbs":9,"title":7},"52":{"body":116,"breadcrumbs":5,"title":3},"53":{"body":89,"breadcrumbs":4,"title":2},"54":{"body":28,"breadcrumbs":4,"title":2},"55":{"body":70,"breadcrumbs":4,"title":2},"56":{"body":28,"breadcrumbs":5,"title":3},"57":{"body":56,"breadcrumbs":4,"title":2},"58":{"body":0,"breadcrumbs":8,"title":6},"59":{"body":45,"breadcrumbs":3,"title":1},"6":{"body":12,"breadcrumbs":3,"title":2},"60":{"body":27,"breadcrumbs":3,"title":1},"61":{"body":235,"breadcrumbs":5,"title":3},"62":{"body":49,"breadcrumbs":4,"title":2},"63":{"body":82,"breadcrumbs":4,"title":2},"64":{"body":29,"breadcrumbs":4,"title":2},"65":{"body":20,"breadcrumbs":6,"title":3},"66":{"body":33,"breadcrumbs":4,"title":3},"67":{"body":47,"breadcrumbs":4,"title":2},"68":{"body":175,"breadcrumbs":4,"title":2},"69":{"body":21,"breadcrumbs":4,"title":2},"7":{"body":81,"breadcrumbs":4,"title":3},"70":{"body":49,"breadcrumbs":4,"title":2},"71":{"body":36,"breadcrumbs":5,"title":3},"72":{"body":108,"breadcrumbs":7,"title":4},"73":{"body":10,"breadcrumbs":5,"title":2},"74":{"body":16,"breadcrumbs":5,"title":2},"75":{"body":0,"breadcrumbs":5,"title":2},"76":{"body":48,"breadcrumbs":7,"title":4},"77":{"body":76,"breadcrumbs":5,"title":2},"78":{"body":29,"breadcrumbs":7,"title":2},"79":{"body":18,"breadcrumbs":6,"title":1},"8":{"body":42,"breadcrumbs":7,"title":3},"80":{"body":55,"breadcrumbs":10,"title":5},"81":{"body":62,"breadcrumbs":8,"title":3},"82":{"body":0,"breadcrumbs":6,"title":1},"83":{"body":31,"breadcrumbs":9,"title":4},"84":{"body":0,"breadcrumbs":3,"title":2},"85":{"body":120,"breadcrumbs":2,"title":1},"86":{"body":163,"breadcrumbs":3,"title":2},"87":{"body":104,"breadcrumbs":7,"title":6},"88":{"body":82,"breadcrumbs":4,"title":3},"89":{"body":114,"breadcrumbs":7,"title":6},"9":{"body":32,"breadcrumbs":5,"title":1},"90":{"body":97,"breadcrumbs":7,"title":6},"91":{"body":285,"breadcrumbs":4,"title":3},"92":{"body":0,"breadcrumbs":3,"title":2},"93":{"body":132,"breadcrumbs":2,"title":1},"94":{"body":0,"breadcrumbs":5,"title":4},"95":{"body":33,"breadcrumbs":2,"title":1},"96":{"body":95,"breadcrumbs":4,"title":3},"97":{"body":81,"breadcrumbs":6,"title":5},"98":{"body":98,"breadcrumbs":2,"title":1},"99":{"body":51,"breadcrumbs":3,"title":2}},"docs":{"0":{"body":"Signal K Server is software designed to be deployed on a vessel to act as a central hub which: Collects data from devices and sensors on board Aggregates and exposes it using the Signal K Data Standard 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 , 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 for 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","breadcrumbs":"Introduction » Introduction","id":"0","title":"Introduction"},"1":{"body":"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: NPM package Docker image GitHub repository See the relevant section below for instructions based on your target system.","breadcrumbs":"Installation » Getting Started","id":"1","title":"Getting Started"},"10":{"body":"Log in to the RPi Desktop and open a terminal. Update the list of install packages. sudo apt update Install NodeJS 18 and npm. Follow instructions for Ubuntu and Debian based distributions like Raspberry Pi OS at NodeSource Distributions . 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. 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","breadcrumbs":"Installation » Installing on Raspberry Pi » Install the Dependencies","id":"10","title":"Install the Dependencies"},"100":{"body":"Create index.js with the following content: 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;\n};","breadcrumbs":"Plugins » Javascript","id":"100","title":"Javascript"},"101":{"body":"Create index.js with the following content: 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;\n} 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. If there are asynchronous operations in your plugin's stop implementation you should return a Promise that resolves when stopping is complete. 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. Return a Promise from stop if needed so that start is not called before stopping is complete. 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: plugin.registerWithRouter = (router) => { router.get('/preferences', (req, res) => { res.status(200).json({ preferences: { color: 'red', speed: 1.23 } }); });\n}; 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: const openapi = require('./openApi.json'); plugin.getOpenApi = () => openapi;","breadcrumbs":"Plugins » Typescript","id":"101","title":"Typescript"},"102":{"body":"A plugin's schema function must return a JSON Schema 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/<plugin-name>.json. (Default value of SIGNALK_NODE_CONFIG_DIR is $HOME/.signalk.) Example: 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 ) for details. The server supports also custom plugin configuration components , bypassing the automatic configuration format generation. The plugin is passed the configuration settings as the first parameter of the start function. plugin.start = (settings, restartPlugin) => { // settings contains the plugin configuration ...\n}","breadcrumbs":"Plugins » Plugin configuration / Schema","id":"102","title":"Plugin configuration / Schema"},"103":{"body":"The plugin can define uiSchema by returning a 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: uiSchema['myObject'] = { 'ui:field': 'collapsible', collapse: { field: 'ObjectField', wrapClassName: 'panel-group' }\n} For more information, see react-jsonschema-form-extras","breadcrumbs":"Plugins » UI Schema","id":"103","title":"UI Schema"},"104":{"body":"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: \"signalk-plugin-enabled-by-default\": true","breadcrumbs":"Plugins » Making a plugin enabled by default","id":"104","title":"Making a plugin enabled by default"},"105":{"body":"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: const openapi = require('./openApi.json');\n... 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: \"servers\": [ { \"url\": \"/myapi/endpoint\" } ], See testplugin for an example.","breadcrumbs":"Plugins » Add an OpenAPI Definition","id":"105","title":"Add an OpenAPI Definition"},"106":{"body":"To record deltas sent by the plugin in the server's data log, enable the Log plugin output in the plugin configuration screen.","breadcrumbs":"Plugins » Logging","id":"106","title":"Logging"},"107":{"body":"Plugins can be removed via the AppStore. You can also remove a plugin manually by: Deleting it's folder under ~/.signalk/node_modules Deleting it's entry from ~/.signalk/package.json Run npm prune from the ~/.signalk/ directory. Alternatively you can: Remove the folder ~/.signalk/node_modules Run npm install from the ~/.signalk/ directory. Finally you can remove the plugin setting file in ~/.signalk/plugin-config-data/.","breadcrumbs":"Plugins » Removing a plugin","id":"107","title":"Removing a plugin"},"108":{"body":"Following are links to some published SignalK plugins that serve as an example of working plugins: set-system-time Ais Reporter","breadcrumbs":"Plugins » Examples","id":"108","title":"Examples"},"109":{"body":"A plugin will generally want to: Subscribe to data published by the server (i.e. received from a NMEA 2000 bus, etc) 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 for details. Using the server API, plugins can either: Get the current value of a path in the full model or 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: { 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' }","breadcrumbs":"Plugins » Processing Data » Processing data from the server","id":"109","title":"Processing data from the server"},"11":{"body":"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\nUsing sample data from /usr/lib/node_modules/signalk-server/samples/plaka.log\nsignalk-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. { \"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\" }\n}","breadcrumbs":"Installation » Installing on Raspberry Pi » Install Signal K Server","id":"11","title":"Install Signal K Server"},"110":{"body":"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. const value = app.getSelfPath('uuid');\napp.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. const baseStations = app.getPath('shore.basestations');","breadcrumbs":"Plugins » Processing Data » Reading the current path value","id":"110","title":"Reading the current path value"},"111":{"body":"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. 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); }); } );\n}; plugin.stop = () => { unsubscribes.forEach(f => f()); unsubscribes = [];\n}; 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.","breadcrumbs":"Plugins » Processing Data » Subscribing to Deltas","id":"111","title":"Subscribing to Deltas"},"112":{"body":"A SignalK plugin can not only read deltas, but can also send them. This is done using the handleMessage() API method and supplying: The plugin id A formatted delta update message The Signal K version ['v1' or 'v2'] (if omitted the default is 'v1') . See REST APIs for details. Example: app.handleMessage( plugin.id, { updates: [{ values: [{ path: 'environment.outside.temperature', value: -253 }] }] }, 'v1' );","breadcrumbs":"Plugins » Processing Data » Sending Deltas","id":"112","title":"Sending Deltas"},"113":{"body":"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: 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: app.emit('nmea2000JsonOut', { pgn: 130306, 'Wind Speed': speed, 'Wind Angle': angle < 0 ? angle + Math.PI*2 : angle, 'Reference': \"Apparent\" });","breadcrumbs":"Plugins » Processing Data » Sending NMEA 2000 data from a plugin","id":"113","title":"Sending NMEA 2000 data from a plugin"},"114":{"body":"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: app.on('nmea2000OutAvailable', () => { app.emit( 'nmea2000out', '2017-04-15T14:57:58.468Z,2,6,126720,%s,%s,4,a3,99,01,00'); });","breadcrumbs":"Plugins » Processing Data » Sending a message on NMEA2000 startup","id":"114","title":"Sending a message on NMEA2000 startup"},"115":{"body":"SignalK server provides an interface to allow plugins to: Discover Features. Access / update the full data model send / receive deltas (updates) Interact with APIs Expose HTTP endpoints These functions are available via the app object passed to the plugin when it is invoked.","breadcrumbs":"Plugins » Server API » Server API for plugins","id":"115","title":"Server API for plugins"},"116":{"body":"app.getFeatures(enabed) Returns an object detailing the available APIs and Plugins. The enabled parameter is optional and has the following values: undefined (not provided): list all features true: list only enabled features false: list only disabled features Example: let features = app.getFeatures(); { \"apis\": [ \"resources\",\"course\" ], \"plugins\": [ { \"id\": \"anchoralarm\", \"name\": \"Anchor Alarm\", \"version\": \"1.13.0\", \"enabled\": true }, { \"id\": \"autopilot\", \"name\": \"Autopilot Control\", \"version\": \"1.4.0\", \"enabled\": false }, { \"id\": \"sk-to-nmea2000\", \"name\": \"Signal K to NMEA 2000\", \"version\": \"2.17.0\", \"enabled\": false }, { \"id\": \"udp-nmea-sender\", \"name\": \"UDP NMEA0183 Sender\", \"version\": \"2.0.0\", \"enabled\": false } ]\n}","breadcrumbs":"Plugins » Server API » Discover Features","id":"116","title":"Discover Features"},"117":{"body":"app.getPath(path) Returns the entry for the provided path starting from the root of the full data model. Example: let baseStations = app.getPath('shore.basestations'); // baseStations:\n{ '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' }\n} app.getSelfPath(path) Returns the entry for the provided path starting from vessels.self in the full data model. Example: let uuid = app.getSelfPath('uuid');\n// 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 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: { state: 'COMPLETED', statusCode: 200\n} or { state:'COMPLETED', statusCode: 400, message:'Some Error Message'\n} 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: { state: 'PENDING' } When the action has completed the handler should call the callback function with the result: callback({ state: 'COMPLETED', statusCode: 200 }) or callback({ state:'COMPLETED', statusCode: 400, message:'Some Error Message'\n}) Example: Synchronous response: function myActionHandler(context, path, value, callback) { if(doSomething(context, path, value)){ return { state: 'COMPLETED', statusCode: 200 }; } else { return { state: 'COMPLETED', statusCode: 400 }; }\n} plugin.start = (options) => { app.registerPutHandler('vessels.self', 'some.path', myActionHandler, 'somesource.1');\n} Example: Asynchronous response: 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' };\n} plugin.start = (options) => { app.registerPutHandler('vessels.self', 'some.path', myActionHandler);\n}","breadcrumbs":"Plugins » Server API » Accessing the Data Model","id":"117","title":"Accessing the Data Model"},"118":{"body":"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: app.handleMessage('my-signalk-plugin', { updates: [ { values: [ { path: 'navigation.courseOverGroundTrue', value: 1.0476934 } ] } ]\n}); Plugins emitting deltas that use Signal K v2 paths (like the Course API 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 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: { path: ..., value: ..., context: ..., source: ..., $source: ..., timestamp: ... } Example: app.streambundle .getBus('navigation.position') .forEach(pos => app.debug(pos)); // output\n{ 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'\n}\n{ 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'\n} app.streambundle.getSelfBus(path) Get a Bacon JS 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: app.streambundle .getSelfBus('navigation.position') .forEach(pos => app.debug(pos)); // output\n{ 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'\n}\n{ 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'\n} app.streambundle.getSelfStream(path) Get a Bacon JS 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: 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: app.streambundle.getAvailablePaths(); // returns\n[ \"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\",\n] 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. app.registerDeltaInputHandler((delta, next) => { delta.updates.forEach(update => { update.values.forEach(pathValue => { if(pathValue.startsWith(\"foo\")) { pathValue.path = \"bar\" } }) }) next(delta)\n});","breadcrumbs":"Plugins » Server API » Working with Deltas","id":"118","title":"Working with Deltas"},"119":{"body":"app.savePluginOptions(options, callback) Save changes to the plugin's configuration options. Example: let options = { myConfigValue = 'Something the plugin calculated'\n}; app.savePluginOptions(options, () => {app.debug('Plugin options saved')}); app.readPluginOptions() Read the stored plugin configuration options. Example: 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: let myDataFile = require('path').join( app.getDataDirPath(), 'somedatafile.ext')","breadcrumbs":"Plugins » Server API » Configuration","id":"119","title":"Configuration"},"12":{"body":"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\nsudo systemctl stop signalk.socket To start Signal K server again enter the following commands: sudo systemctl start signalk.service\nsudo systemctl start signalk.socket To stop Signal K server from starting automatically enter the following commands: sudo systemctl disable signalk.service\nsudo systemctl disable signalk.socket You are ready to now configure your installation and connect data from devices on your boat.","breadcrumbs":"Installation » Installing on Raspberry Pi » Run the Setup Script","id":"12","title":"Run the Setup Script"},"120":{"body":"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: app.setPluginStatus('Initializing');\n// Do something\napp.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: 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 . 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: app.reportOutputMessages(54);","breadcrumbs":"Plugins » Server API » Messages and Debugging","id":"120","title":"Messages and Debugging"},"121":{"body":"app.getSerialPorts() => Promise<Ports> This returns a Promise which will resolve to a Ports object which contains information about the serial ports available on the machine.","breadcrumbs":"Plugins » Server API » Serial Port","id":"121","title":"Serial Port"},"122":{"body":"app.registerResourceProvider(ResourceProvider) Used by Resource Provider plugins to register each resource type it handles. See Resource Provider Plugins for details. app.resourcesApi.getResource(resource_type, resource_id, provider_id?) Retrieve 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. Most commonly used for creating a new resource entry when more than one provider is registered for the specified resource type. returns: Promise<any> Promise for the resource. Example: app.resourcesApi.getResource( 'waypoints', 'ac3a3b2d-07e8-4f25-92bc-98e7c92f7f1a' }\n).then ( () => { // success ...\n}).catch (error) { // handle error console.log(error.message); ...\n} app.resourcesApi.setResource(reso