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:
- A class within plugin.py (the name should be Plugin)
- 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', } ] } - The constructor must expect one parameter.
When calling the constructor AvNav will provide an instance of the API as the parameter. - 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. ...
This will refer to a layout that you registered with the name
"main"."layoutName": "$prefix$.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.