@welytics/clearship-signalk
v0.2.1
Published
Beta: sends vessel telemetry (position, wind, depth, battery) from Signal K to ClearShip for remote monitoring
Maintainers
Readme
@welytics/clearship-signalk
⚠️ Beta
This plugin is an early beta (
0.1.0-beta.6). It sends the current vessel state at a fixed interval and records the sailed track. Anchor watch and alerting are not included yet.Track points are buffered on disk while the boat is offline and sent once it is back in range; live readings are buffered in memory only and lost when the Signal K server restarts. There is no notification yet if the connection drops. Feedback is very welcome.
Signal K server plugin that sends vessel telemetry to ClearShip: position, course, speed, wind, depth, temperature, pressure and battery state.
It lets you see your boat on the map in ClearShip even when you are not aboard.
A ClearShip account is required.
Installation
Via the Signal K Appstore (recommended)
- Open your Signal K server → Appstore → search for
clearship - Install ClearShip Telemetry
- Restart the server when Signal K asks you to
Or manually on the server
cd ~/.signalk
npm install @welytics/clearship-signalk
sudo systemctl restart signalk # or restart the server your usual waySetup
- In ClearShip: Boat → Live → Connect boat → Create token The token is shown only once — copy it.
- In Signal K: Server → Plugin Config → ClearShip Telemetry
- Paste the token, enable the plugin, save.
The plugin status line then reads Connected — 12 measurements in 1 frame(s) via delta stream.
The position appears in ClearShip within a minute.
The number of measurements in the status line is the number to watch. If you see a warning instead of a number, the plugin reaches the server fine but finds no data in Signal K — see Troubleshooting.
Settings
| Option | Default | Meaning |
|---|---|---|
| apiBase | https://clearship.app/api | For local testing e.g. http://192.168.1.50:3000/api |
| deviceToken | — | The device token created in ClearShip |
| intervalSeconds | 60 | Send interval, minimum 10 s. The server may adjust it. |
| trackPoints | true | Record the sailed track and the swing at anchor |
| trackSampleSeconds | 5 | How often the position is checked for a new track point |
| groups | all on | Which data groups are sent |
| customPaths | empty | Per-field path override, see below |
| customMetrics | empty | Extra readings ClearShip has no field for, see below |
Track recording
With trackPoints enabled the plugin records the sailed track alongside the
regular telemetry. A point is written when the boat
- has moved more than 50 m, or
- has changed course by more than 15° (so tacks stay sharp), or
- is under way and a minute has passed, or
- is at rest and ten minutes have passed.
The resting points matter more than they look: they are the swing cloud that lets ClearShip tell an anchorage from a berth without you pressing anything. A boat on her lines barely moves and hardly changes heading; a boat at anchor weathervanes with every windshift.
Track points travel in the same frames as the telemetry — same endpoint, same token. They cost roughly 144 extra frames per day at rest, and while sailing as many as the course demands.
Offshore without a connection, track points go into a backlog on disk
(track-backlog.jsonl in the plugin's data directory) that survives restarts
and power cuts. Once the boat is back in range the backlog is sent oldest first
in chunks of 500, up to 5,000 points a minute. It holds 50,000 points — more
than a week of continuous sailing; beyond that, older points are thinned
rather than dropped, so the whole route stays on the map. ClearShip accepts
buffered track points up to 30 days old.
Setting customPaths.shorePower makes the distinction certain rather than
inferred: you cannot plug in at anchor. Signal K has no standard path for it,
so it has to be set by hand — look for it under electrical.* in the Data
Browser.
| listPaths | false | Log every path seen on vessels.self — use it to find sensor paths |
| dryRun | false | Diagnostic mode: log frames to the Signal K log, send nothing |
Sensors on non-standard paths (RUUVI, custom sensors)
Not every sensor publishes to the standard path. A RUUVI tag, for instance,
publishes under the location name configured in its own plugin — a tag set up as
cabin ends up on environment.cabin.pressure, not
environment.outside.pressure, so this plugin would not find it.
Two ways to fix that, either is fine:
- Map it here. Enter the path under
customPaths→Pressure. Unit conversion still applies — Signal K is SI everywhere, so Pa still becomes hPa no matter which path the value came from. - Map it in Signal K. Change the sensor plugin's location, or remap the path server-side. Cleaner in the long run, because every other Signal K consumer benefits too — but it changes your vessel's data model.
To find the right path, enable listPaths and restart the plugin. It writes
every path it has seen on vessels.self to the log:
All 87 paths seen on vessels.self:
environment.cabin.humidity = 0.54
environment.cabin.pressure = 101820
environment.cabin.temperature = 297.45
...The same information is in the Signal K Data Browser — listPaths is just
faster when you already have the log open.
Dry run is the fastest way to see what would be transmitted without any data leaving the boat — useful before connecting for the first time. It reports the same warnings as normal operation.
Extra readings (tank levels, revolutions, shore power)
The fields below cover what every boat has. Everything else differs from vessel
to vessel, so it is configured rather than hard-coded. Add an entry under
customMetrics for each one:
| Field | Meaning |
|---|---|
| path | Signal K path, e.g. tanks.freshWater.0.currentLevel |
| label | Name shown on the tile, e.g. Fresh water |
| unit | Displayed as-is: l, %, A, bar, rpm, °C |
| factor / offset | value × factor + offset, applied on board |
| key | Optional identifier. Defaults to the path — changing it later creates a second tile instead of renaming the first. |
factor and offset exist because Signal K is SI throughout. A tank level
arrives as a ratio, so factor: 100 with unit: "%" gives a readable tile; a
fridge arrives in Kelvin, so offset: -273.15 with unit: "°C" does. Converting
here rather than in the cloud means the stored value and the displayed value are
the same number.
Each reading appears in ClearShip as its own tile the first time it arrives — no configuration needed on that side. Name, unit, decimals, order and whether it shows in the live view, on the dashboard or both are then editable there, per boat.
Signal K paths that are read
| Path | Field | Conversion |
|---|---|---|
| navigation.position | lat, lon | — |
| navigation.speedOverGround | sog | m/s → kn |
| navigation.courseOverGroundTrue | cog | rad → ° |
| navigation.headingTrue | .headingMagnetic | heading | rad → ° |
| environment.wind.speedApparent / .angleApparent | awsKn, awaDeg | m/s → kn, rad → ° |
| environment.wind.speedTrue / .directionTrue | twsKn, twdDeg | m/s → kn, rad → ° |
| environment.depth.belowTransducer | depthM | — |
| environment.water.temperature | waterTempC | K → °C |
| environment.outside.temperature | airTempC | K → °C |
| environment.outside.pressure | pressureHpa | Pa → hPa |
| electrical.batteries.*.voltage | batteryV | — |
| electrical.batteries.*.capacity.stateOfCharge | batterySoc | ratio → % |
How values are read
Two mechanisms, in this order:
- Full model (
app.getSelfPath) — preferred, because the source priorities configured in your Signal K server apply. With several GPS sources on board the position stays stable instead of jumping between antennas. - Delta stream — fallback. On some servers the full model returns nothing
even though the data is clearly present in the Data Browser (observed on
Victron Venus OS). The plugin therefore also subscribes to the
vessels.selfdelta stream and caches the latest value per path. The trade-off: where a path has several sources, last-received wins instead of the configured priority.
The status line names which one is in use — via full model or
via delta stream — so you can tell at a glance whether source priorities are
being honoured.
Only vessels.self is read. AIS targets of other vessels are never transmitted.
Troubleshooting
| Status line | Cause |
|---|---|
| No measurements: neither the full model nor the delta stream provides data | No instrument data is reaching the boat's Signal K. Check the Data Browser for any navigation.* or electrical.* data under vessels.self. The cause is upstream of this plugin (NMEA connection, gateway, instruments powered off). |
| No expected paths yet — the delta stream has N other path(s) | Signal K has data, but not on the paths this plugin reads. Send the path list from the Data Browser and it can be mapped. |
| No measurements in the selected groups. Signal K only provides: … | The data is there, but the matching group is disabled in the plugin config |
| Cannot read Signal K (…) | Bug in the plugin or an incompatible server version — please report |
| No device token configured | Token missing in the plugin config |
| Token invalid or revoked | Token deleted or copied incorrectly — create a new one in ClearShip |
| A ClearShip subscription is required | Telemetry is not included in your current plan |
| Offline — N frames, M track points buffered | No internet connection; frames are resent on the next attempt, track points survive a restart |
| Connected — … catching up: M track points left | Back in range, the offline backlog is being delivered |
| Server error 5xx | ClearShip unreachable; frames stay buffered |
Detailed logs live in the Signal K server under Server → Server Log. The
plugin additionally logs via debug (DEBUG=clearship-signalk), including a
one-off inventory at startup of which expected paths your server does and does
not provide.
Local development
cd ~/.signalk
npm link /path/to/clearship/packages/signalk-plugin
# restart Signal K; the plugin appears in Plugin ConfigThe ingest endpoint can also be tested without Signal K:
curl -X POST http://localhost:3000/api/telemetry/ingest \
-H "Authorization: Bearer csk_live_..." \
-H "Content-Type: application/json" \
-d '{"frames":[{"t":"'"$(date -u +%Y-%m-%dT%H:%M:%SZ)"'","lat":43.1234,"lon":6.5432,"sog":0.2,"cog":187,"twsKn":12.4,"twdDeg":225,"depthM":6.2,"batterySoc":87}]}'License
MIT
