openwebrxplus-plugins

Development Guide

How to write receiver plugins for current OpenWebRX+ versions, and how to add them to this repository.

Quickstart

The example plugin README has a step-by-step quickstart, a minimal plugin skeleton, a table of available events, and annotated patterns for the most common tasks (event listening, function wrapping, DOM access).

  1. Create $OWRX_FOLDER/plugins/receiver/my_plugin/my_plugin.js.
  2. Load it locally by folder name: await Plugins.load('my_plugin');
  3. Export Plugins.my_plugin.init() — return true on success, false on a failed dependency check.

Plugin Structure

Plugin Loader API

Provided by OpenWebRX+ (htdocs/plugins.js). The receiver loads plugins/receiver/init.js when the page DOM is ready.

Function Description
Plugins.load(name_or_url) Load a plugin. A plain name loads plugins/receiver/<name>/<name>.js, a URL loads that file. Calls init() and loads <name>.css unless no_css is set. Returns a Promise
Plugins.isLoaded(name, version) Truthy when the plugin is loaded and its _version is at least version
Plugins._load_script(url) / Plugins._load_style(url) Load an extra script or stylesheet. Return a Promise
Plugins._enable_debug = true Print loader debug messages to the console

Native Plugin UI API

Provided by OpenWebRX+ (htdocs/lib/Plugins.js). Check that a function exists before using it, and keep a fallback when the plugin must also work on older versions.

Plugins.addButton(id, title, handler, color)

Adds a button to the plugin button stack next to the receiver panel and returns the button element.

Plugins.addWindow(id, title, content)

Creates a floating, draggable and resizable window and returns the window element. The window starts hidden.

Plugins.toggleWindow(id, on)

Shows (on = true), hides (on = false) or toggles (no on) the window.

Plugins.addSection(id, title, content)

Adds a collapsible section to the receiver panel, before the Settings section, and returns the content element (.openwebrx-section).

var content = Plugins.addSection('my_plugin', 'My Plugin');
content.appendChild(myControls);
// open it by default when the user has not chosen yet
if (!LS.has('plugin-section-my_plugin')) Plugins.toggleSection('my_plugin', true);

Plugins.toggleSection(id, on)

Opens (on = true), closes (on = false) or toggles (no on) the section. The state is saved in localStorage.

Built-in Plugins

OpenWebRX+ ships optional plugins as global objects with an init() method: MapPlugin, SunPlugin, KeyPlugin and RigPlugin (see the Built-in Plugins table).

utils Plugin API

utils (current version 0.9) is the shared helper plugin. Require the version that introduced the function you use: Plugins.isLoaded('utils', 0.9).

Function Since Description
wrap_func(name, before_cb, after_cb, obj) 0.1 Wrap the function obj[name] (default obj is window). before_cb(orig, thisArg, args) returns true to call the original; after_cb(result) can change the return value
on_ready(callback) 0.4 Call callback once OpenWebRX+ has finished initializing the page (document.owrx_initialized)
deepMerge(target, ...sources) 0.5 Deep-merge objects into target; multiple sources applied left-to-right are supported since 0.9
fillTemplate(template, variables) 0.5 Replace {name} placeholders with values
findCommonPrefix(strings) 0.6 Longest common prefix of an array of strings
observe_mutations(targets, options, callback, run_now) 0.8 MutationObserver setup for one or more targets; returns handles
disconnect_observers(handles) 0.8 Disconnect handles from observe_mutations()

Events triggered on document by utils (listen with $(document).on(...)):

See the utils README for details and examples.

notify Plugin API

notify shows short notifications on the receiver page.

Plugin Options

Plugins that take options from init.js provide a setup(options) method, called after the plugin is loaded:

await Plugins.load('https://0xaf.github.io/openwebrxplus-plugins/receiver/my_plugin/my_plugin.js');
Plugins.my_plugin.setup({ color: 'red' });

Adding a New Plugin to This Repository

All receiver plugins are listed in receiver/plugins.json. This manifest is the single source of truth: the plugin_loader plugin reads it at runtime, and the plugin tables in the README are generated from it.

  1. Create receiver/<name>/<name>.js, and <name>.css if needed.
  2. Create receiver/<name>/README.md with the Jekyll frontmatter and a ## Code section linking to the Github repo (see any existing plugin).
  3. Add an entry to receiver/plugins.json. The order of entries is the order of the README tables.
  4. Run python3 tools/plugins.py. It validates the manifest, checks that every plugin folder is listed, and regenerates the README tables.
  5. Commit the plugin folder, receiver/plugins.json and README.md together.

Do not edit the README tables between <!-- plugins:...:start --> and <!-- plugins:...:end --> by hand. Change plugins.json and run the script. python3 tools/plugins.py --check only validates and fails if the README is outdated.

Manifest entry fields:

Field Required Description
id yes Plugin folder name, the global name of a built-in plugin (MapPlugin), or a unique name for a third-party plugin
category yes builtin, receiver, utility, deprecated, experimental or thirdparty. Experimental plugins are not listed in the README
description yes One line, Markdown allowed. Shown in the README and in the loader
author no Contributor name, rendered as a link to Contributors
requires no Plugin ids loaded before this plugin, e.g. ["utils"]
conflicts no Plugin ids that cannot run together with this plugin
replaced_by no Built-in plugin that replaces a deprecated plugin; the loader hides the deprecated plugin when the built-in exists
homepage third-party Project page of a third-party plugin, used as the README link
url no Third-party only: https:// link to the plugin .js file. Without it the plugin is listed in the README but not in the loader
global built-in Global object of the built-in plugin, e.g. MapPlugin
since built-in First OpenWebRX+ version with the built-in plugin
detect no Built-ins only: CSS selector that exists once the plugin is started, when it does not create #plugin-button-<name>, #plugin-section-<name> or #plugin-window-<name>
setup no Set to required when the plugin cannot work without admin configuration. plugin_loader hides it until plugin_options[id] exists, then calls Plugins.<name>.setup(plugin_options[id]) after loading it

When a new built-in plugin appears in OpenWebRX+, add it with "category": "builtin" and also add a commented-out line for it in receiver/init.js.sample.

Third-party plugins live in other repositories. Add them with "category": "thirdparty" and a homepage. Add url only when the plugin is a single .js file that works with Plugins.load() and needs no server-side setup; only then can users enable it from the loader.

Map plugins are not part of the manifest; their table is edited by hand.

Hosting on GitHub

To host plugins on GitHub, use GitHub Pages for correct JS Content-Type.

Plugins hosted on a different server than OpenWebRX+ load fine with Plugins.load(), but fetch() requests (for example plugin_loader reading plugins.json) need the Access-Control-Allow-Origin header. GitHub Pages sends it.