Files
venus/watermaker/MQTT_API.md
dev 9756538f16 Initial commit: Venus OS boat addons monorepo
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
2026-03-16 17:04:16 +00:00

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');
```