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
642 lines
15 KiB
Markdown
642 lines
15 KiB
Markdown
# 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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"mode": "dts",
|
|
"timestamp": "2026-01-04T10:30:00"
|
|
}
|
|
```
|
|
|
|
### `watermaker/production/stopped`
|
|
|
|
Published when production ends.
|
|
|
|
**Retained: No | QoS: 1**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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**
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{
|
|
"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.
|
|
|
|
```json
|
|
{}
|
|
```
|
|
|
|
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).
|
|
|
|
```json
|
|
{}
|
|
```
|
|
|
|
### `watermaker/command/cancel_limit`
|
|
|
|
Cancel production limit tracking without stopping production. No payload required.
|
|
|
|
```json
|
|
{}
|
|
```
|
|
|
|
### `watermaker/command/response`
|
|
|
|
Responses to commands are published here.
|
|
|
|
**Success:**
|
|
|
|
```json
|
|
{
|
|
"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:**
|
|
|
|
```json
|
|
{
|
|
"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)
|
|
|
|
```javascript
|
|
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');
|
|
```
|