Organizes 11 projects for Cerbo GX/Venus OS into a single repository: - axiom-nmea: Raymarine LightHouse protocol decoder - dbus-generator-ramp: Generator current ramp controller - dbus-lightning: Blitzortung lightning monitor - dbus-meteoblue-forecast: Meteoblue weather forecast - dbus-no-foreign-land: noforeignland.com tracking - dbus-tides: Tide prediction from depth + harmonics - dbus-vrm-history: VRM cloud history proxy - dbus-windy-station: Windy.com weather upload - mfd-custom-app: MFD app deployment package - venus-html5-app: Custom Victron HTML5 app fork - watermaker: Watermaker PLC control UI Adds root README, .gitignore, project template, and per-project .gitignore files. Sensitive config files excluded via .gitignore with .example templates provided. Made-with: Cursor
15 KiB
Watermaker MQTT API Reference
Reference for UI/dashboard developers integrating with the watermaker system over MQTT.
Connection
| Parameter | Default | Environment Variable |
|---|---|---|
| Broker host | 198.18.4.108 |
MQTT_BROKER_HOST |
| Broker port | 1883 |
MQTT_BROKER_PORT |
| SSL/TLS | false |
MQTT_BROKER_SSL |
| Topic prefix | watermaker |
MQTT_TOPIC_PREFIX |
All topics below use the default watermaker prefix.
Availability (Last Will and Testament)
Subscribe to watermaker/availability to detect when the watermaker API is online or offline.
| Value | Meaning |
|---|---|
online |
API is connected and publishing |
offline |
API disconnected (set automatically by broker via LWT) |
This topic is retained -- you receive the last known state immediately on subscribe.
Data Topics (Server to Dashboard)
Real-time PLC data. Payloads are JSON.
watermaker/status
System status. Published when status changes (mode, step, operating mode, valve positioning, or active timer changes).
Retained: Yes | QoS: 0
{
"system": {
"mode": 7,
"mode_name": "DTS",
"operating_mode": "dts",
"status": 3,
"status_name": "Running",
"valve_positioning": false,
"active_timer": null
},
"connection": {
"connected": true,
"last_update": "2026-01-04T10:30:00Z"
}
}
| Field | Type | Description |
|---|---|---|
system.mode |
int | Raw R1000 mode register |
system.mode_name |
string | Human-readable mode name |
system.operating_mode |
string | idle, single_pass, double_pass, dts, fw_flush |
system.status |
int | Production step (R1036): 0=Idle, 1=Prime, 2=Init, 3=Run, 4=Stop, 5=Flush |
system.status_name |
string | Human-readable step name |
system.valve_positioning |
bool | True during valve positioning phase |
system.active_timer |
object/null | Currently active timer info |
connection.connected |
bool | PLC connection status |
watermaker/sensors
Sensor readings. Published every 1-2s during operation, every 60s when idle.
Retained: No | QoS: 0
{
"pressure": {
"feed": 45.2,
"membrane": 820.5,
"product": 12.3
},
"temperature": {
"feed": 75.4,
"system": 78.2
},
"flow": {
"brine": 2.8,
"product_1": 1.2,
"product_2": 1.0
},
"quality": {
"ppm": 245
}
}
| Field | Type | Unit | Description |
|---|---|---|---|
pressure.feed |
float | PSI | Feed water pressure |
pressure.membrane |
float | PSI | Membrane pressure |
pressure.product |
float | PSI | Product water pressure |
temperature.feed |
float | F | Feed water temperature |
temperature.system |
float | F | System temperature |
flow.brine |
float | GPM | Brine flow rate |
flow.product_1 |
float | GPM | First pass product flow |
flow.product_2 |
float | GPM | Second pass product flow |
quality.ppm |
int | PPM | Total dissolved solids |
watermaker/timers
Timer values. Published every polling cycle.
Retained: No | QoS: 0
{
"production_timer": {
"priming": 165.5,
"initialize": 6553.5,
"ppm_stabilization": 6553.5,
"stop": 6553.5,
"flush": 6553.5,
"sw_valve_positioning": 6553.5,
"dts_valve_positioning": 6553.5,
"fwf": 6553.5
},
"fwf_timer": {
"countdown": 6553.5
}
}
A value of 6553.5 indicates the timer is inactive.
watermaker/counters
Water production counters. Published every 30s during production, 60s when idle.
Retained: Yes | QoS: 0
{
"single_pass_since_last": 125.4,
"double_pass_since_last": 0.0,
"dts_since_last": 450.2,
"total_product": 12500.8,
"total_brine": 25001.6
}
| Field | Type | Unit | Description |
|---|---|---|---|
single_pass_since_last |
float | gallons | Single pass production since last reset |
double_pass_since_last |
float | gallons | Double pass production since last reset |
dts_since_last |
float | gallons | DTS production since last reset |
total_product |
float | gallons | Lifetime product water total |
total_brine |
float | gallons | Lifetime brine total |
watermaker/outputs
Digital output states. Published every polling cycle.
Retained: Yes | QoS: 0
{
"low_pressure_pump": 1,
"high_pressure_pump": 1,
"boost_pump": 0,
"product_solenoid_1": 1,
"product_solenoid_2": 0,
"brine_solenoid": 1
}
Values: 1 = on, 0 = off.
watermaker/tank_update
Fresh water tank levels from Victron Venus OS. Published every 10 seconds.
Retained: Yes | QoS: 0
{
"connected": true,
"portal_id": "abc123",
"tanks": {
"bow": {
"name": "Water Bow",
"level_percent": 72.5,
"capacity_gallons": 90.0,
"gallons_stored": 65.25,
"last_update": 1704361800.0
},
"stern": {
"name": "Water Stern",
"level_percent": 45.0,
"capacity_gallons": 90.0,
"gallons_stored": 40.5,
"last_update": 1704361800.0
}
},
"total_gallons": 105.75,
"total_capacity": 180.0,
"total_percent": 58.75,
"timestamp": "2026-01-04T10:30:00"
}
Event Topics (Server to Dashboard)
One-time event notifications. Payloads are JSON.
watermaker/production/started
Published when production begins.
Retained: No | QoS: 1
{
"mode": "dts",
"timestamp": "2026-01-04T10:30:00"
}
watermaker/production/stopped
Published when production ends.
Retained: No | QoS: 1
{
"mode": "dts",
"timestamp": "2026-01-04T10:30:00"
}
watermaker/production/limit
Production limit tracking update. Published every cycle during active production.
Retained: No | QoS: 0
{
"is_tracking": true,
"mode": "dts",
"run_type": "fill",
"gallons_produced": 23.5,
"elapsed_seconds": 720,
"gallon_limit": 50.0,
"time_limit_seconds": null,
"progress_percent": 47.0,
"eta_seconds": 800,
"flow_rate_gpm": 2.1,
"tank_fill": {
"bow_level": 72.5,
"stern_level": 45.0,
"total_remaining_gallons": 64.25,
"target_level": 90,
"eta_seconds": 1830
}
}
watermaker/production/limit_reached
Published when a production limit is reached.
Retained: No | QoS: 1
{
"type": "gallon",
"value": 50.0,
"reason": "Gallon limit of 50.0 gallons reached",
"timestamp": "2026-01-04T11:00:00"
}
watermaker/verification/state_verified
Published when a predicted state transition was correct.
Retained: No | QoS: 1
{
"command_type": "start",
"verification_time_ms": 150.25,
"timestamp": "2026-01-04T10:30:00Z"
}
watermaker/verification/state_correction
Published when the actual state differs from the prediction.
Retained: No | QoS: 1
{
"expected": {
"status": 0,
"status_name": "Valve Positioning",
"operating_mode": "dts"
},
"actual": {
"status": 1,
"status_name": "Priming",
"operating_mode": "dts"
},
"command_type": "start",
"timestamp": "2026-01-04T10:30:00Z"
}
watermaker/verification/timer_correction
Published when a timer drifts more than 2 seconds from its expected value.
Retained: No | QoS: 0
{
"timer_id": "priming",
"actual_value": 165.5,
"expected_value": 168.0,
"drift_seconds": 2.5,
"timestamp": "2026-01-04T10:30:00Z"
}
watermaker/remote_change
Published when a state change is detected from the physical HMI panel (not from API or dashboard).
Retained: No | QoS: 1
{
"type": "remote_start",
"previous_step": 0,
"current_step": 1,
"previous_mode": null,
"current_mode": "single_pass",
"message": "Production started externally (single_pass)",
"timestamp": "2026-01-04T10:30:00"
}
type Value |
Description |
|---|---|
remote_start |
Production started from HMI |
remote_stop |
Production stopped from HMI |
remote_skip |
Phase skipped from HMI |
remote_mode_change |
Operating mode changed from HMI |
remote_step_change |
Step changed from HMI |
flush_complete |
Flush completed |
watermaker/consumable_warning
Published when a consumable approaches or exceeds its replacement limit.
Retained: No | QoS: 1
{
"item": "pre_filter",
"name": "Pre-Filter",
"level": 85.2,
"remaining": 14.8,
"unit": "gallons",
"severity": "warning",
"timestamp": "2026-01-04T10:30:00"
}
severity |
Trigger |
|---|---|
warning |
80%+ of lifespan consumed |
critical |
100%+ of lifespan consumed |
watermaker/alarm
Published when a PLC alarm is detected.
Retained: No | QoS: 1
{
"alarm_code": "HIGH_PRESSURE",
"message": "High pressure alarm triggered",
"timestamp": "2026-01-04T10:30:00Z"
}
watermaker/error
Published on system errors.
Retained: No | QoS: 1
{
"message": "PLC communication timeout",
"timestamp": "2026-01-04T10:30:00"
}
Command Topics (Dashboard to Server)
Publish to these topics to control the watermaker. Commands are only processed when MQTT_COMMANDS_ENABLED=true (disabled by default for safety).
Responses are published to watermaker/command/response with QoS 1.
watermaker/command/start
Start a watermaker production sequence.
{
"mode": "dts",
"run_type": "fill",
"gallon_limit": 50.0,
"time_limit": 3600,
"expected_gallons": 45.0
}
| Field | Type | Required | Description |
|---|---|---|---|
mode |
string | Yes | single_pass, double_pass, dts, or fw_flush |
run_type |
string | No | fill, gallons, timer, boat_wash, or manual |
gallon_limit |
float | No | Max gallons before auto-stop (not valid for fw_flush) |
time_limit |
int | No | Max seconds before auto-stop |
expected_gallons |
float | No | Pre-safety-margin estimate for Fill mode |
Mode descriptions:
| Mode | Description |
|---|---|
single_pass |
Seawater through single membrane |
double_pass |
Seawater through both membranes |
dts |
Dockside Treatment System (freshwater source) |
fw_flush |
Fresh water flush (maintenance cycle) |
Run type descriptions:
| Run Type | Description |
|---|---|
fill |
Run until tanks reach target level (uses expected_gallons) |
gallons |
Run until gallon_limit is reached |
timer |
Run until time_limit is reached |
boat_wash |
Short run for deck wash water |
manual |
Manual run, no auto-stop |
watermaker/command/stop
Stop current production. No payload required.
{}
The API automatically selects the correct stop command based on the current production step (priming/init stop, running stop, or flush stop).
watermaker/command/skip
Skip the current phase. No payload required. Only valid during priming (step 1) or initializing (step 2).
{}
watermaker/command/cancel_limit
Cancel production limit tracking without stopping production. No payload required.
{}
watermaker/command/response
Responses to commands are published here.
Success:
{
"success": true,
"command": "start",
"mode": "dts",
"message": "DTS sequence started successfully",
"predicted_state": {
"system": {
"status": 0,
"status_name": "Valve Positioning",
"operating_mode": "dts"
}
},
"timestamp": "2026-01-04T10:30:00Z"
}
Error:
{
"success": false,
"error": "INVALID_MODE",
"message": "Invalid mode. Valid modes: single_pass, double_pass, dts, fw_flush",
"timestamp": "2026-01-04T10:30:00Z"
}
Update Frequencies
| Topic | Idle | Operating | Notes |
|---|---|---|---|
sensors |
60s | 1-2s | Every 60s when idle for monitoring; spread polling when operating |
timers |
5s | 1-2s | Spread polling alternates even/odd seconds |
status |
On change | On change | Only published when fields change |
counters |
60s | 30s | |
outputs |
5s | 1-2s | |
tank_update |
10s | 10s | From Victron Venus OS |
production/limit |
-- | Every cycle | Only during active production |
WebSocket to MQTT Migration
For UI developers migrating from Socket.IO to MQTT.
Event Mapping
| Socket.IO Event | MQTT Topic | Retained |
|---|---|---|
status |
watermaker/status |
Yes |
sensors |
watermaker/sensors |
No |
timers |
watermaker/timers |
No |
counters |
watermaker/counters |
Yes |
outputs |
watermaker/outputs |
Yes |
state_verified |
watermaker/verification/state_verified |
No |
state_correction |
watermaker/verification/state_correction |
No |
timer_correction |
watermaker/verification/timer_correction |
No |
remote_change |
watermaker/remote_change |
No |
production_started |
watermaker/production/started |
No |
production_stopped |
watermaker/production/stopped |
No |
production_limit |
watermaker/production/limit |
No |
limit_reached |
watermaker/production/limit_reached |
No |
tank_update |
watermaker/tank_update |
Yes |
consumable_warning |
watermaker/consumable_warning |
No |
alarm |
watermaker/alarm |
No |
error |
watermaker/error |
No |
Key Differences from WebSocket
- Retained messages replace the on-connect full state push. New MQTT subscribers automatically receive the last known value for retained topics (status, counters, outputs, tank_update).
- No explicit connect/subscribe handshake. Subscribe to the topics you need. Use
watermaker/#to receive everything. - Wildcard subscriptions are supported:
watermaker/production/#for all production events,watermaker/verification/+for all verification events. - Commands go through MQTT publish instead of REST POST endpoints. The payload and validation logic are identical.
- Availability monitoring via
watermaker/availability(retained LWT) replaces Socket.IO connect/disconnect events.
Quick Start (JavaScript with MQTT.js)
import mqtt from 'mqtt';
const client = mqtt.connect('mqtt://198.18.4.108:1883');
client.on('connect', () => {
client.subscribe('watermaker/#');
});
client.on('message', (topic, message) => {
const data = JSON.parse(message.toString());
switch (topic) {
case 'watermaker/status':
updateStatus(data);
break;
case 'watermaker/sensors':
updateSensors(data);
break;
case 'watermaker/timers':
updateTimers(data);
break;
case 'watermaker/counters':
updateCounters(data);
break;
case 'watermaker/tank_update':
updateTanks(data);
break;
case 'watermaker/availability':
updateConnectionStatus(message.toString());
break;
}
});
// Start production
function startProduction(mode, options) {
client.publish('watermaker/command/start', JSON.stringify({
mode: mode,
run_type: options.run_type,
gallon_limit: options.gallon_limit,
time_limit: options.time_limit,
expected_gallons: options.expected_gallons
}));
}
// Listen for command responses
client.subscribe('watermaker/command/response');