Skip to content

RotorHazard Plugin: Operator Setup

How to use RaceLink inside RotorHazard once the RaceLink_RH_Plugin is installed. For installation itself (online vs. offline), see README.md.

Audience. Operators running RotorHazard who want to use RaceLink to control the LED nodes during a race.

The plugin registers two panels in RotorHazard's UI:

  • rl_settings — RaceLink device and group configuration. Devices, group assignment, "force groups" action, defaults.
  • rl_quickset — quick-fire controls. Group selector, preset selector, brightness slider, and the Run quickset action.

Both panels are bootstrapped on first plugin sync and are guarded by _settings_panel_bootstrapped / _quickset_panel_bootstrapped flags so repeated syncs do not produce RHUI Redefining ... log spam.

The full mapping of which UI element refreshes on which state-scope token (GROUPS, DEVICES, DEVICE_MEMBERSHIP, DEVICE_SPECIALS, PRESETS) is in ../host/ARCHITECTURE.md §"UI Scope Matrix".

Button What it does
rl_btn_set_defaults Apply preset defaults across the fleet
rl_btn_force_groups Re-broadcast every device's stored groupId — the recovery action when nodes have been reflashed or moved between gateways
rl_btn_get_devices Run Discover Devices
rl_run_autodetect Trigger the auto-detect workflow
run_quickset Apply the currently-selected preset and brightness to the selected group

The quickset drop-downs (group, preset) re-populate via state-scope events; if a drop-down looks stale, click the RaceLink Refresh button or trigger any other action that fires a state update.

Race-event integration

RotorHazard fires events on Evt.STARTUP, Evt.SHUTDOWN, Evt.RACE_START, Evt.RACE_FINISH, Evt.RACE_STOP etc. The plugin's onStartup is wired to claim the gateway via discoverPort({}); onShutdown releases it via transport.close().

Race-event-bound effects. The plugin's ActionEffect hooks let you bind RaceLink scenes or presets to RotorHazard race events (e.g. "fire scene Race Start on Evt.RACE_START"). The binding shape is currently RotorHazard-side configuration — see the RotorHazard documentation for ActionEffect basics.

Gateway ownership in plugin mode

The plugin owns the USB-LoRa gateway exclusively. RotorHazard does not open the dongle; only the plugin does. Consequence:

  • Do not run racelink-standalone and the plugin against the same dongle. The second one fails with PORT_BUSY.
  • On RotorHazard restart, the plugin re-claims the port. If a WebUI tab is open during the restart, it briefly shows the "Gateway not available" banner; it self-clears once discoverPort succeeds.

For the architecture-level rule see ../host/ARCHITECTURE.md §"Gateway Ownership".

What the plugin does NOT do

Some host-side features are only exposed in the shared WebUI (mounted at /racelink inside the RotorHazard process), not in the RotorHazard side panels. These include:

  • The Scenes page (full editor with cost estimator).
  • The OTA / Firmware Update dialog.
  • The RL Presets dialog.
  • The Specials dialog per device.

Open the RaceLink WebUI by navigating to <rotorhazard-host>:5000/racelink (or whatever path RotorHazard mounts the plugin at — typically /racelink). The shared WebUI is the same UI that racelink-standalone exposes.

Online vs. offline installation

The plugin supports two installation modes; both load racelink-host from the same RotorHazard Python environment:

Mode What happens
Online RotorHazard installs the plugin and resolves the host wheel from the immutable Git tag declared in the manifest. Requires internet.
Offline The release ZIP contains the host wheel under custom_plugins/racelink_rh_plugin/offline_wheels/. On first plugin start, the bundled host wheel is installed locally before the plugin continues. The first offline start can take a moment.

See README.md §"Installation Modes" for the full flow. The online installation uses a git+https://... dependency format because that is what RHFest accepts — see ADR-0001.

Troubleshooting

  • "RaceLink Gateway is not available" stays red after a RotorHazard restart. Click Retry connection in the WebUI. The plugin's startup hook claims the gateway on Evt.STARTUP; if the event sequence misfires, the explicit retry forces another discoverPort attempt.
  • PORT_BUSY on plugin start. Another process owns the dongle. Most commonly racelink-standalone left running. Stop it, then Retry connection.
  • RaceLink panels are stale or blank. A state-scope sync failure during plugin bootstrap. Restart RotorHazard once; the plugin's bootstrap re-registers the panels.
  • RHUI Redefining ... log spam. Should not happen post-D4 (audit batch). If it does, the bootstrap-flag mechanism in the plugin's ui.py is regressed; file a bug.