signalk-sailorwind-plugin
v0.3.0
Published
SignalK server plugin that pushes weather observations to sailorwind.net
Maintainers
Readme
signalk-sailorwind-plugin
A SignalK server plugin that pushes weather observations from a boat's SignalK server to the sailorwind marine-weather observation service. Once installed and configured with a device token, it aggregates the last 10 minutes of wind / pressure / temperature / humidity samples for the self vessel and POSTs them — fully offline-tolerant, with a persistent retry queue so a flaky cellular link doesn't drop data.
Prerequisites
- A SignalK Node server (>= v1.x). Tested against the standard appstore install.
- A sailorwind account.
- A device token — generate one at https://sailorwind.net/settings/api-tokens. Tokens are prefixed
slw_dev_and should be treated as secrets.
Install
In the SignalK admin UI:
- Open the Appstore tab.
- Search for sailorwind.
- Click Install on
signalk-sailorwind-plugin. - Open the Server → Plugin Config tab and find sailorwind weather submission.
Or from the command line on the SignalK server host:
cd ~/.signalk
npm install signalk-sailorwind-plugin
# then restart the SK serverConfigure
In Server → Plugin Config → sailorwind weather submission:
| Field | Notes |
| --- | --- |
| sailorwind API base URL | Default https://sailorwind.net. Leave as-is unless you've been told otherwise (e.g. during a private test deployment). |
| Device token | Paste the slw_dev_... value from the sailorwind settings page. Treat as a secret. |
| Fields to submit | Check the boxes for the observations you want to share. Position is always included — it's required by the API and is what makes an observation useful. |
Hit Submit, then toggle the plugin Enabled.
What gets sent
Every 10 minutes (not user-configurable in v1), the plugin computes one aggregated observation from the samples received during the window and POSTs it to /v1/observations:
- position — latest GPS fix in the window (from
navigation.position) - wind speed — arithmetic mean of
environment.wind.speedTrue(m/s) - wind gust — max of
environment.wind.speedTrueover the window - wind direction — a true compass bearing the wind is from (degrees TRUE, meteorological convention), resolved from whatever your stack publishes (see below)
- pressure — latest
environment.outside.pressure(Pa → hPa) - air temp — latest
environment.outside.temperature(K → °C) - water temp — latest
environment.water.temperature(K → °C) - humidity — latest
environment.outside.humidity(0..1 → %)
How wind direction is resolved
Boats expose wind direction in different shapes, so the plugin tries, in order, and stops at the first that works — using the least derivation possible and trusting any value you already provide:
environment.wind.directionTrue— used directly.environment.wind.directionMagnetic+ magnetic variation → true.- A true wind angle off the bow (
environment.wind.angleTrueWater/…Damped) + boat heading → true bearing. Each wind sample is paired with the nearest-in-time heading, so a mid-window tack is handled correctly.
Heading comes from navigation.headingTrue if present, otherwise navigation.headingMagnetic lifted to true. Magnetic variation comes from navigation.magneticVariation if your GPS publishes it, otherwise it is computed from your position and the date using the World Magnetic Model (WMM2025) — so no manual variation input is ever required.
If a window genuinely yields no resolvable true direction (e.g. a wind angle but no heading source at all), the plugin skips that submission rather than send an un-renderable, direction-less observation. It never substitutes a forecast or guessed direction. Enable both wind speed and wind direction in the config — the server requires both on every observation.
Offline behaviour
- Observations are queued to a JSON file under the plugin's data directory (
~/.signalk/plugin-config-data/sailorwind/queue.jsonon a typical install). - Up to 100 entries (~16 hours of submissions at 10-minute cadence) are buffered. When the cap is hit, oldest entries are dropped first.
- On reconnect, the queue drains FIFO at the next 10-minute tick.
- Each entry carries a stable idempotency key, so a retry after a successful-but-unack'd send is deduped server-side.
Status
The status line in the plugin admin UI tells you everything:
Awaiting first submission— plugin is up but the first 10-minute window hasn't fired yet.Last sent 2 min ago • queue: 0— healthy; data is reaching sailorwind.Last sent 47 min ago • queue: 5— queue is growing; the API is unreachable (offline, DNS, TLS, or server outage). Will drain on reconnect.Misconfigured: missing apiToken— token is empty; paste your device token.
Troubleshooting
Where are the plugin logs?
SignalK writes plugin logs to its main log (commonly ~/.signalk/signalk-server.log or via journalctl -u signalk on a service install). Look for lines starting with sailorwind.
permanent 401 dropping entry
The API rejected your token. Most common causes:
- Token typo on paste (extra space, missing prefix).
- Token was revoked in the sailorwind UI.
- Token belongs to a different account than the vessel you're trying to submit for.
Generate a fresh token at https://sailorwind.net/settings/api-tokens and paste it in.
permanent 400
The server rejected the body shape. Usually means a value is out of range (e.g. wind speed > 100 m/s from a stuck anemometer). The full error body is logged. Check your sensors.
network error, will retry
Transient — the plugin couldn't reach the server. It will retry at the next 10-minute tick. No action needed; check connectivity if it persists.
Submission cadence is wrong for me
The 10-minute cadence is fixed in v1. The aggregation window matches sailorwind's grid coarsening — sending faster doesn't get you finer-grained data on the map.
Data, privacy, and license
Observations submitted via this plugin are public data on sailorwind, subject to position coarsening as described in the sailorwind data license. The plugin itself transmits only what you opted into via the Fields to submit checkboxes, plus position (always required) and an observation timestamp.
The device token is stored in plain text in your SignalK server's config (the same place every other SK plugin stores its config). Don't share screenshots of your plugin-config UI with the token visible.
Internal dependency
This plugin imports its aggregation / submission pipeline from the sibling internal package sailorwind-integration-core (the same code path the upcoming Capacitor mobile wrap will use). The dep is wired via file:../shared for monorepo dev; for an npm release the shared lib is bundled into the plugin's dist/ before publish. End users installing from the SignalK appstore don't need to know it's there.
License
Apache-2.0. See LICENSE or https://www.apache.org/licenses/LICENSE-2.0.
