Plugins

Avnav Plugins

=== not for android ===

Plugins extend the functionality of AvNav. They can extend server functions (using python code) as well as the WebApp using Java Script or CSS.

Each plugin has to reside in a separate directory. The directory name defines the plugin name. 2 root directories are scanned by AvNav in search for plugins:

  • "systemdir" - a directory to hold plugins installed for all users of a system (e.g. via a package). This is /usr/lib/avnav/plugins.
  • "userdir" - a directory for plugins specific for a particular user. This is a sub directory of the user's "data dir" - /home/pi/avnav/data/plugins on the pi, $HOME/avnav/plugins on other Linux systems.

Beside those two an additional directory holds internal plugins (builtin).

In principle a plugin's server part can read and write data from/to AvNav at various interfaces. The WebApp parts would normally display such values or simply add new display functions to AvNav or adapt its look and feel.

Up to 3 files within a plugin directory are processed by AvNav. Additional files in the directory will be ignored by AvNav - however they can be required by the plugin itself.

Those files are:

  • plugin.py -  the server parts, optional
  • plugin.js - the Java Script parts, optional
  • plugin.css - the CSS parts, optional

There is a complete example for a plugin on GitHub.

Installation

To create an own plugin you can either provide it as a zip file or a debian package.
If providing a zip file it should at top level contain one subdirectory with the name of the plugin (do not include "plugin" or "avnav" in this name).
A user of the plugin would need to unpack the zip file in it's AvNav data dir, sub dir plugins (e.g. /home/pi/avnav/data/plugins on a raspberry pi).
This way the plugin would become a "user plugin"

If you are providing a debian package it should be named like avnav-xxx-plugin. The content should be unpacked to /usr/lib/avnav/plugins/<pluginName>.
This way the plugin will be come a "system plugin".
Plugin packages should contain the name of the plugin (i.e. the directory name) as a meta data field "avnav-plugin".
Example:

avnav-plugin: system-obp-plotterv3

This will help the avnav-updater to correctly identify whether a plugin package should be shown or not.
When you set the meta data field "avnav-hidden" to true, the package will only be shown from the avnav-updater if explicitely enabled.

List of Plugins

  • Seatalk Remote - in combination with the seatalk remote control from AK-Homberger
  • History - Data history and display
  • Update - update AvNav (and related packages) eliminating the need for command line access
    Also integrates a config file editor and a log viewer for AvNav
  • MapProxy - integrate MapProxy to access and download from various online chart sources
  • Obp-RC-Remote - plugin using the IR Remote from Christian
  • More-NMEA-Plugin - add decoders and computation for course and wind data
  • rudder-angle - show the rudder angle (from SignalK)
  • Obp-PlotterV3 - plugin for all special functions for the Open Boat Projects 10 inch plotter (V3)

plugin.js

Java Script code file offering same functions as described in user specific Java Script code .

plugin.css

CSS code file offering same options as described in user specific CSS.

plugin.py

Overview

The diagram provides a rough overview of the AvNav server's internal structure together with the points (interfaces) where plugins can interact.

Point Function Example
A Feed NMEA data to the internal list. These become available at all outputs.
Hint: a decoder is required for the WebApp to access them
Read from a sensor and generate the correct NMEA0183 sentence.
B Reading of inbound NMEA data. Here you can access (potentially using a filter) all NMEA data passing through AvNav. Together with point "C": decoding of NMEA sentences.
C Feed data into the internal store of AvNav. Data in the internal store is available in a tree like structure. Each leaf has a key in the form of "a.b.c....". Example: "gps.lat".
All keys starting with "gps...." are sent to the WebApp automatically and become available there as "nav.gps...." - see layout editor and user specific Java Script.
Keys need to be registered by the plugin before using them. It is not possible to override keys defined by AvNav itself. Exception: "allowKeyOverride" set to true by user.
Write the data from a sensor like  gps.temperature.outside or write decoded NMEA data
D Reading of data from the internal store Computing of derived data (and writing them back at "C") - or just send values somewhere externally.
E Handling of HTTP requests The java script parts may send a HTTP request to be handled in the python code. Normally a dictionary will be returned (as json).

A plugin.py example is available at GitHub.

Basic Structure

To be recognized by AvNav the plugin has to provide:

  1. A class within plugin.py (the name should be Plugin)
  2. The class must contain a static method (@classmethod) with the name pluginInfo. It must return a dictionary.
    * description (mandatory) * data: list of keys to be stored (optional) * path - the key - see AVNApi.addData, all pathes starting with "gps." will be sent to the GUI * description

    An example could look like:
    @classmethod def pluginInfo(cls):
    return { 'description': 'a test plugins', 'data': [ { 'path': 'gps.test', 'description': 'output of testdecoder', } ] }
  3. The constructor must expect one parameter.
    When calling the constructor AvNav will provide an instance of the API as the parameter.
  4. The class must implement a run method (without parameters).
    After initialization this method will be called in a separate thread.
    Normally you would have an endless loop here to provide the plugin functionality.

You can provide parameters for the plugin in avnav_server.xml. They can be read at the API using getConfigValue.

Plugin API

The API provides the following functions:

Function Description
log,debug,error Logging functions. Lines will be written to the AvNav log file. Avoid writing too many log and error entries. This would flood the log and obfuscate important entries (example: do not write an error every second)
getConfigValue get a config value from avnav_server.xml.
fetchFromQueue Interface B: read data from the internal NMEA list. There is an example in the API code. The filter option is working the same way as in  avnav_server.xml.
addNMEA Interface A: feed a NMEA record to the internal list. You can leave the check sum to be computed by AvNav. You can also block decoding within AvNav. The source parameter is the channel name you would use in blackList parameters .
addData Interface C: feed data into the internal store. You can only write data with keys that have been announced by the return values of the pluginInfo method.
getSingleValue Interface D: read data from the internal store. To combine multiple reads there is the method getDataByPrefix
setStatus provide the current state of the plugin. This will be shown at the status page.
registerUserApp You can register a user app as a plugin. You need an URL and an icon file. The icon file should be located in the plugin directory. You can use $HOST inside the URL. It will be replaced by the AvNav server's address. Example in the signalk plugin.
registerLayout If the plugin provides its own widgets it could make sense to provide a layout for the user to select. Just save the layout in the plugin directory after creating it with the layout editor. Example within the signalk plugin.
registerSettingsFile
(since 20220225)
Register an own settings file (that has been previously saved from the settings page).
The file name (second parameter) is relative to the plugin dir. The name (first parameter) is shown to the user.
Within this file you can use $prefix$ in the layout name if you want to refer to a layout that you register from the same plugin.
...
"layoutName": "$prefix$.main"
....
This will refer to a layout that you registered with the name "main".
getDataDir The data directory of AvNav.
registerChartProvider If the plugin provides charts you need to register a callback here to return a list of charts.
registerRequestHandler If the plugin handles HTTP requests (interface E) you need to register a callback here. The URL triggering the callback is:
<pluginBase>/api
pluginBase is the value returned by getBaseUrl (/plugins/<name>).
The java script parts can compute the API url using the variable AVNAV_BASE_URL: AVNAV_BASE_URL+"/api"
In the simple case your callback should return a dictionary that is sent back as json.
getBaseUrl returns the base URL for the plugin
registerUsbHandler
(since 20201227)
registers a callback for an USB device. With this registration in place AvNav will not further care about this particular device. The provided callback will be called with the device path as a parameter as soon as the USB device has been detected.
You can easily figure out the USB id by watching the status page when connecting the device. The USB id is bound to the USB socket - see AVNUsbSerialReader. Using this api a plugin can easily handle a serial device by its own. An example you can find on GitHub.
getAvNavVersion
(since 20210115)
return the current version of AvNav (integer value). Can be used to test if certain functions can be expected to be available
saveConfigValues
(since 20210322)
Store the plugin's config values in avnav_server.xml. The parameter must by a dictionary holding the values to be changed. The plugin must ensure that it can restart with the parameters provided here.
registerEditableParameters
(since 20210322)
This registers a list of config values which can be changed during runtime. The first parameter must be a list of dictionary objects describing the parameters, the second parameter provides a callback to be called with the changed parameters (will typically call saveConfigValues).
For the parameter descriptions refer to the source code.
registerRestart
(since 20210322)
Register a stopCallback that will allow to disable the plugin.
unregisterUserApp
(since 20210322)
unregister a previously registered user app
deregisterUsbHandler
(since 20210322)
unregister an usb device id (see registerUsbHandler)
shouldStopMainThread
(since 20210322)
can be used in the main loop of the plugin to check if it should stop. Do not call this from other threads as it always returns True.
sendRemoteCommand
(since 20230426)
Send a remote control command, see the source code for details.
registerSettingsFile
(since 20230426)
Register a file with store user settings. Those settings can be loaded by the user.
registerCommand
(since 20230426)
Register a command that can be executed by AvNav. This can be used to replace existing commands or register new commands. See the source code or the AVNCommandHandler config for details.

Enabling and Disabling of System Plugins

(since 20230426)

To be able to disable plugins that are installed with debian packages, there is a script /usr/lib/avnav/plugin.sh.
You can call this script (as root) with to maintain the visibility of system plugins and even set some default parameters.
Just call the script with no parameters to get a help.

Special Functions for Raspberry Pi

(since 20230426)

Plugins that are provided as debian packages for the raspberry pi can provide a shell script "plugin-startup.sh".
This script will allow plugins to configure system parameters.
It will always be called during the boot process of the system.
whether a plugin script is called or not depends on a parameter in the avnav.conf file (see Image preparation). The parameter name is:
AVNAV_<PLUGIN>
with plugin being the plugin name (i.e. name of it's directory) all translated to upper case and all characters except digits and letters a-z being removed.
If thisparameter is set to "yes" the plugin script will be called.

There are 3 ways it can be called:

plugin-startup.sh enable

This will be used when the plugin becomes active (first boot with parameter set in avnav.conf) for the first time.
This plugin should now make all necessary changes on the system (if possible it should be able to revert them, see below).
Typically those are changes in /boot/config.txt or other files.
The script should return 1 to indicate the need for a reboot, 0 otherwise or < 0 for errors.

There are some helper functions available that can be used by this script. You can include those helpers using

. "$AVNAV_SETUP_HELPER"

The environment variable AVNAV_SETUP_HELPER is set up before calling the script.
For an example script refer to the obp-plotterv3-plugin.

plugin-startup.sh disable

This call will be made when the parameter in avnav.conf changes from yes to anything else. The script should revert it's changes that have been made to the system - as far as possible.
Remark: As the process is normally only intended to be used once when the image starts for the first time it should not be a real problem if not all changes can be reverted.

plugin.startup.sh [no parameters]

This will be called on any boot. You should not try to modify system settings that require a reboot or some restarts afterwards as this could be very surprising to the user if on an arbitrary reboot suddenly things are changed on the system.