Skip to content

eSpa MQTT Reference

TIP

eSpa supports MQTT Home Assistant auto-discovery. Entities for temperature, pumps, lights, blower, heatpump, and timers are automatically published when the spa and MQTT broker are connected.

This document summarizes the MQTT topics and payloads used by eSpa for both status reporting and setting spa state.

TIP

The eSpa firmware must be configured with the correct MQTT broker settings to use these features. Refer to the getting started guide for details on how to set up MQTT.

Topic Structure by Firmware Version

Breaking change in v2

The MQTT base topic changed in eSpa v2.0.2-beta. Firmware v1.x published under the spa serial number; v2 publishes under the device ID. Any integration written against v1 topics must be updated, and the old topics will stop updating after the upgrade.

v1.xv2.0.2-beta and later
Base topicsn_esp32/<spaSerialNumber>/eSpa/<deviceId>/
IdentifierSpa serial number, e.g. 24483862-24490698Device ID, e.g. 54C7F4FEFF0A
Statussn_esp32/<spaSerialNumber>/statuseSpa/<deviceId>/status
Commandssn_esp32/<spaSerialNumber>/set/<property>eSpa/<deviceId>/set/<property>
Availabilitysn_esp32/<spaSerialNumber>/availableeSpa/<deviceId>/available
Raw controller response(not available)eSpa/<deviceId>/rfResponse

Finding your identifier:

  • v1.x — the spa serial number (e.g. 2449XXXX-2449XXXX), shown in the eSpa web interface. See the getting started guide.
  • v2 — the device ID, a 12-character uppercase hex string derived from the ESP32's MAC address (e.g. 54C7F4FEFF0A). The easiest way to find it is to subscribe to eSpa/# on your broker and look at the topics that arrive. It is also the client ID the spa uses to connect, so it appears in most brokers' client lists, and is written to the debug log at boot as MQTT base topic is eSpa/<deviceId>/.

Reading the rest of this page

Every topic below is written as <baseTopic>/…. Substitute the base topic for your firmware:

  • v1.xsn_esp32/<spaSerialNumber>, for example sn_esp32/24483862-24490698/status
  • v2eSpa/<deviceId>, for example eSpa/54C7F4FEFF0A/status

Anything that only exists on one firmware version is marked with a badge, like this: v2 only

Upgrading from v1

After upgrading, the v1 available topic keeps its retained offline last-will message, so anything still subscribed to sn_esp32/# will show the spa as permanently offline. Clear it once with a retained empty message:

bash
mosquitto_pub -h <broker> -t 'sn_esp32/<spaSerialNumber>/available' -r -n

Status Topic

  • Topic: <baseTopic>/status
  • Description: Publishes a JSON object with the current spa status. The structure includes all relevant properties, such as temperatures, power, pumps, blower, lights, timers, heat pump, controller info, firmware, and more. See below for a real-world example and field descriptions.
  • Publishing: Sent on change (the firmware publishes from the spa interface update callback), plus once when the spa and broker first connect. In practice this is roughly once a minute on an idle spa.
  • Retained: No. A client that subscribes has to wait for the next update before it sees any data, so don't expect an immediate value on connect.

Example Payload (v1.x):

json
{
  "temperatures": {
    "setPoint": 36,
    "water": 36.2,
    "heater": 34.9,
    "case": 40,
    "heatpumpAmbient": 0,
    "heatpumpCondensor": 0
  },
  "power": {
    "voltage": 303,
    "current": 0,
    "power": 0,
    "totalenergy": 0
  },
  "status": {
    "heatingActive": "ON",
    "ozoneActive": "ON",
    "state": "Heating",
    "spaMode": "NORM",
    "controller": "SVM2",
    "firmware": "V6.24.03.20",
    "serial": "244XXXX-2XXXXX98",
    "siInitialised": "true",
    "mqtt": "connected",
    "datetime": "2025-09-18 11:06:58",
    "dayOfWeek": "Thursday"
  },
  "eSpa": {
    "model": "spa-control-pcb",
    "update": {
      "installed_version": "v1.0.11-alpha-10-ga642ece-nightly-2025-08-15"
    }
  },
  "heatpump": {
    "mode": "Auto",
    "auxheat": "OFF"
  },
  "pumps": {
    "pump1": {
      "installed": false,
      "speedType": "-",
      "possibleStates": [],
      "state": "OFF",
      "speed": 0
    },
    "pump2": {
      "installed": true,
      "speedType": "1",
      "possibleStates": ["AUTO"],
      "state": "ON",
      "speed": 2
    },
    "pump3": {
      "installed": true,
      "speedType": "1",
      "possibleStates": ["OFF","ON"],
      "state": "OFF",
      "speed": 0
    },
    "pump4": {
      "installed": true,
      "speedType": "1",
      "possibleStates": ["OFF","ON"],
      "state": "OFF",
      "speed": 0
    },
    "pump5": {
      "installed": false,
      "speedType": "-",
      "possibleStates": [],
      "state": "OFF",
      "speed": 0
    }
  },
  "blower": {
    "state": "OFF",
    "mode": "Variable",
    "speed": "0"
  },
  "sleepTimers": {
    "timer1": {
      "state": "Off",
      "begin": "07:00",
      "end": "22:30"
    },
    "timer2": {
      "state": "Off",
      "begin": "07:00",
      "end": "22:30"
    }
  },
  "lights": {
    "speed": 5,
    "state": "OFF",
    "effect": "Step",
    "brightness": 5,
    "color": { "h": 180, "s": 100 },
    "color_mode": "hs"
  }
}

Example Payload (v2):

v2 keeps the same overall shape and adds several sections. Additions are marked below.

json
{
  "temperatures": {
    "setPoint": 38.6,
    "water": 38.6,
    "heater": 38.6,
    "case": 38,
    "heatpumpAmbient": 15,
    "heatpumpCondensor": 9
  },
  "power": {
    "voltage": 306,
    "vmax": 23,
    "clmt": 10,
    "current": 0,
    "power": 0,
    "totalenergy": 0,
    "heatElementCurrent": 0
  },
  "status": {
    "heatingActive": "ON",
    "ozoneActive": "ON",
    "state": "Heating",
    "spaMode": "NORM",
    "controller": "SVM2",
    "firmware": "V6.24.03.20",
    "serial": "244XXXXX-244XXXXX",
    "siInitialised": "true",
    "mqtt": "connected",
    "datetime": "2025-09-18 11:06:58",
    "dayOfWeek": "Thursday"
  },
  "eSpa": {
    "model": "espa-v2",
    "update": { "installed_version": "v2.0.2-beta" }
  },
  "heatpump": { "mode": "Auto", "auxheat": "OFF" },
  "filtration": { "blockDuration": 3, "hours": 4, "wclnTime": "09:161" },
  "lockmode": "Unlocked",
  "pumps": {
    "pump1": { "installed": false, "speedType": "-", "state": "OFF", "speed": 0 },
    "pump2": { "installed": true, "speedType": "1", "possibleStates": ["AUTO"], "state": "ON", "speed": 2 },
    "pump3": { "installed": true, "speedType": "1", "possibleStates": ["OFF", "ON"], "state": "OFF", "speed": 0 },
    "pump4": { "installed": true, "speedType": "1", "possibleStates": ["OFF", "ON"], "state": "OFF", "speed": 0 },
    "pump5": { "installed": false, "speedType": "-", "state": "OFF", "speed": 0 }
  },
  "blower": { "state": "OFF", "mode": "Off", "speed": "0" },
  "sleepTimers": {
    "timer1": { "state": "Off", "begin": "07:00", "end": "22:30" },
    "timer2": { "state": "Off", "begin": "07:00", "end": "22:30" }
  },
  "powerSave": { "level": "Low", "begin": "07:00", "end": "20:00" },
  "lights": {
    "speed": 5,
    "state": "OFF",
    "effect": "Step",
    "brightness": 5,
    "color": { "h": 0, "s": 100 },
    "color_mode": "hs"
  }
}

New in the v2 payload:

  • power.vmax, power.clmt, power.heatElementCurrent — current limit settings and heat element current draw.
  • powerSavelevel, begin, end.
  • filtration.wclnTime — water clean cycle start time. The rest of filtration was already present in v1.
  • status.datetime and status.dayOfWeek are unchanged, but note the controller's clock drifts and may need resetting.

Field Descriptions:

  • temperatures: Set point, water, heater, case, heat pump ambient/condensor.
  • power: Voltage, current, power and total energy. v2 adds vmax, clmt and heatElementCurrent.
  • status: Heating/ozone active, spa state/mode, controller, firmware, serial, initialisation, MQTT, date/time, day of week.
  • eSpa: model is the hardware/build variant the firmware was compiled for — espa-v1, espa-v2 or esp32dev on current builds, and spa-control-pcb on older ones. update.installed_version is the firmware version.
  • heatpump: Mode and aux heat.
  • pumps: Each pump has installed status, speed type, possible states, current state, and speed.
  • blower: State, mode, speed.
  • sleepTimers: Each timer has state, begin, end.
  • lights: Speed, state, effect, brightness, color (hue/saturation), color mode.
  • filtration: Block duration and hours. v2 adds wclnTime (water clean cycle start).
  • lockmode: Current control panel lock state.
  • powerSave v2 only: Level, begin and end times.

Detecting which firmware you are talking to

Use eSpa.update.installed_version (for example v2.0.2-beta) to decide which topic structure applies. Do not use eSpa.model — that reports the board variant it was built for, and an espa-v2 board can still be running v1.x firmware.

Malformed times in v2.0.2-beta

The 2.0.2-beta firmware sometimes reports times with an out-of-range minute component, such as 22:155, 09:161 or 20:100. This affects sleepTimers.*.begin / .end, powerSave.begin / .end and filtration.wclnTime. Validate these against HH:MM before using them, rather than assuming they always parse.

Raw Controller Response Topic v2 only

  • Topic: <baseTopic>/rfResponse
  • Description: The raw, unparsed status string returned by the SpaNET controller over the serial link, published alongside each status update. Useful for debugging, for accessing registers eSpa does not yet decode, and for reporting issues.
  • Payload: A multi-line RF: block of comma-delimited register rows, from ,R2, to ,RG,. Not every letter is used — the controller emits R2R7, R9, RARC, and RERG.
  • Retained: No.
RF:
,R2,0,306,38,306,5,20,44,13,7,5,2024,386,0,1,0,0,373,0,6000,...
,R3,10,1,4,4,4,SW V6 24 03 20,SVM2,24483862,24490698,...
,R4,NORM,0,0,0,4,0,653,0,20,...

WARNING

The register layout is controller firmware specific and is not a stable API. Prefer the parsed status topic for automation.

Complete Property Reference

Spa settings are changed by publishing to <baseTopic>/set/<property>. The table below lists every property the firmware accepts, checked against the source for both v1.0.18 and v2.0.2-beta. Properties marked v2 were added in v2 and are not available on v1.x; everything else works on both.

TIP

The spa controller subscribes to <baseTopic>/set/#, so any property below can be set by publishing to that path. Each one is explained in more detail under Command Topics.

WARNING

Only these exact property names are handled. Anything else is logged as Unhandled property and ignored, with no error sent back to you.

PropertyPayloadNotes
temperatures_setPoint38.0Degrees Celsius, one decimal place
pumpN_stateON / OFFN = 1–5
pumpN_speed1, 2, 31 = Off, 2 = Low, 3 = High. Variable-speed pumps only
pumpN_modeAuto / ManualAny value other than Auto is treated as Manual (set to Low)
blower_stateON / OFF
blower_speed0, or 150 turns the blower off; 15 sets the speed
blower_modeVariable, Ramp, Off
lights_stateON / OFF
lights_effectWhite, Color, Fade, Step, Party
lights_brightness15
lights_speed15Effect cycle speed
lights_color180,100Comma-separated hue,saturation. Hue snaps to 15° steps (0360)
heatpump_modeAuto, Heat, Cool, Off
heatpump_auxheatON / OFF
status_spaModeNORM, ECON, AWAY, WEEK
status_datetime2025-08-15 20:50:10
status_dayOfWeekFriday
sleepTimers_N_stateOff, Everyday, Weekends, Weekdays, or a day nameN = 1 or 2
sleepTimers_N_begin22:00
sleepTimers_N_end07:00
powerSave_level v2Off, Low, High
powerSave_begin v207:00
powerSave_end v220:00
filtration_blockDuration1, 2, 3, 4, 6, 8, 12, 24Hours
filtration_hoursIntegerFiltration run hours
wclnTime v209:00Water clean cycle start time
lock_modeUnlocked, Partially Locked, LockedControl panel lock
vmax v2IntegerMaximum current (amps)
clmt v2IntegerCurrent limit (amps)
keypad_up v2Any valueSends an Up key press; used to wake the spa

The following sections detail the available commands.

Command Topics

The sections below explain each command in turn: what it does, when you'd use it, and the exact topic and payload. All of them use the <baseTopic>/set/<property> form from the table above.

Water Temperature

Set the target water temperature for your spa. The spa will automatically heat or cool to reach this temperature. This is the main way to control the desired temperature for bathing.

  • Topic: <baseTopic>/set/temperatures_setPoint
  • Payload: 38.0

Pump State

Control the ON/OFF state of each pump. Useful for activating or deactivating water jets. Most spas have multiple pumps, each addressed individually. If a pump is not present, commands to its topic will be ignored.

  • Topic: <baseTopic>/set/pumpN_state (where N is the pump number, e.g., pump1_state, pump2_state, etc.)
  • Payload: ON or OFF
  • Notes: Most spas have between 0 and 5 pumps. You can control each pump individually by publishing to its corresponding topic. If a pump is not present, commands to its topic will be ignored.

Pump Speed

Set the speed of a specific pump, if your spa supports variable-speed pumps. Higher speeds increase water flow and jet intensity. Single-speed pumps will ignore this command.

  • Topic: <baseTopic>/set/pumpN_speed (where N is the pump number)
  • Payload: 1 (Off), 2 (Low), 3 (High)
  • Notes: Only pumps that support variable speed will respond to speed commands. For single-speed pumps, this topic may be ignored. Note that 1 turns the pump off rather than setting it to its lowest speed — if you only want to switch a pump on and off, use pumpN_state instead.

Pump Mode

Switch a pump between automatic and manual control.

  • Topic: <baseTopic>/set/pumpN_mode (where N is the pump number)
  • Payload: Auto or Manual
  • Notes: Any payload other than Auto is treated as Manual, which sets the pump to Low to match the speed shown in Auto.

Blower State

Turn the air blower ON or OFF. The blower injects air into the water for a bubbling effect, enhancing the spa experience.

  • Topic: <baseTopic>/set/blower_state
  • Payload: ON or OFF

Blower Speed

Set the speed of the air blower, if your spa supports variable-speed blowers. Higher speeds produce more bubbles. This command is ignored if your spa only supports a fixed-speed blower.

  • Topic: <baseTopic>/set/blower_speed
  • Payload: 0 to turn the blower off, or 1 to 5 to set the speed
  • Notes: Values outside this range are ignored. Only available if your spa has a variable-speed blower.

Blower Mode

Switch the blower between its supported modes. Variable mode allows speed adjustment, Ramp cycles the speed, and Off disables the blower.

  • Topic: <baseTopic>/set/blower_mode
  • Payload: Variable, Ramp, or Off
  • Notes: On v1.x only Variable is recognised — any other payload, including Off, selects Ramp. Use blower_state to turn the blower off on v1.

Light Effect

Change the lighting effect in your spa, such as color cycling, fading, or other supported effects. Effects depend on your spa's lighting capabilities and can enhance ambiance.

  • Topic: <baseTopic>/set/lights_effect
  • Payload: White, Color, Fade, Step, or Party

Light State

Turn the spa lights ON or OFF. Useful for ambiance, safety, or visibility during night use. This is a simple way to control lighting without changing color or brightness.

  • Topic: <baseTopic>/set/lights_state
  • Payload: ON or OFF

Light Brightness

Set the brightness level of the spa lights, from 1 (dim) to 5 (bright). Adjusting brightness can help set the mood or save energy.

  • Topic: <baseTopic>/set/lights_brightness
  • Payload: 1 to 5

Light Speed

Set how quickly the lighting effect cycles.

  • Topic: <baseTopic>/set/lights_speed
  • Payload: 1 to 5

Light Color

Set the color of the spa lights using hue and saturation values. Send a JSON object with h (hue) and s (saturation). This allows for custom color selection to match your preference or theme.

  • Topic: <baseTopic>/set/lights_color
  • Payload: 180,100 — a comma-separated hue,saturation pair, where hue is 0360 and saturation is 0100. This matches the Home Assistant MQTT light hs_command_topic format.
  • Notes: The hue is snapped down to the nearest 15° step. Only the portion before the comma is actually used — saturation is parsed but ignored, since the spa's lights are always fully saturated.

WARNING

Earlier revisions of this page documented a JSON payload of the form { "h": 1485, "s": 100 }. That was wrong on both counts: no released firmware has ever accepted JSON here, and the spa only ever reports hue in the range 0360. If you have an integration publishing JSON to this topic, it has never worked — switch it to the comma-separated form above.

Spa Mode

Change the operating mode of the spa, such as Normal, Away, or other supported modes. Modes may affect heating, filtration, and energy usage, and are useful for vacation or energy-saving schedules.

  • Topic: <baseTopic>/set/status_spaMode
  • Payload: NORM, ECON, AWAY, or WEEK

Sleep Timer State

Set the state of a sleep timer (e.g., Everyday, Off) for automatic scheduling of spa operation. Each timer can be configured independently to automate when the spa is active or in sleep mode.

  • Topic: <baseTopic>/set/sleepTimers_N_state (where N is the timer number, e.g., sleepTimers_1_state, sleepTimers_2_state)
  • Payload: Off, Everyday, Weekends, Weekdays, or a single day name (MondaySunday)
  • Notes: Most spas have two sleep timers. You can control each timer individually by publishing to its corresponding topic. If a timer is not present, commands to its topic will be ignored.

Sleep Timer Begin/End

Set the start and end times for a sleep timer. This allows you to automate when the spa is active or in sleep mode, helping save energy and ensure the spa is ready when you need it.

  • Topic: <baseTopic>/set/sleepTimers_N_begin (where N is the timer number)
  • Payload: 22:00
  • Topic: <baseTopic>/set/sleepTimers_N_end (where N is the timer number)
  • Payload: 07:00

Heatpump Mode

Set the operating mode of the heat pump, such as heating, cooling, automatic, or off. This controls how the spa manages water temperature and can optimize energy use.

  • Topic: <baseTopic>/set/heatpump_mode
  • Payload: Auto, Heat, Cool, or Off

Aux Heat

Enable or disable the auxiliary heat function, if your spa supports it. Aux heat provides additional heating power when needed, such as during rapid heat-up or cold weather.

  • Topic: <baseTopic>/set/heatpump_auxheat
  • Payload: ON or OFF

Date/Time

Set the spa controller's internal date and time. Keeping accurate time is important for scheduling, automation, and logging events.

  • Topic: <baseTopic>/set/status_datetime
  • Payload: 2025-08-15 20:50:10

Day of Week

Set the current day of the week on the spa controller. Useful for weekly scheduling, automation, and ensuring timers run on the correct days.

  • Topic: <baseTopic>/set/status_dayOfWeek
  • Payload: Friday

Power Save Level v2 only

Limit the spa's power draw during a nominated window, typically to avoid peak electricity tariffs.

  • Topic: <baseTopic>/set/powerSave_level
  • Payload: Off, Low, or High

Power Save Begin/End v2 only

Set the window during which the power save level applies.

  • Topic: <baseTopic>/set/powerSave_begin and <baseTopic>/set/powerSave_end
  • Payload: 07:00

Filtration Block Duration

Set how many hours the filtration cycle is blocked for.

  • Topic: <baseTopic>/set/filtration_blockDuration
  • Payload: 1, 2, 3, 4, 6, 8, 12, or 24

Filtration Hours

Set the number of hours the filtration pump runs.

  • Topic: <baseTopic>/set/filtration_hours
  • Payload: An integer number of hours

Water Clean Time v2 only

Set the time of day the water clean cycle starts.

  • Topic: <baseTopic>/set/wclnTime
  • Payload: 09:00

Lock Mode

Lock the spa's physical control panel to prevent unwanted changes.

  • Topic: <baseTopic>/set/lock_mode
  • Payload: Unlocked, Partially Locked, or Locked

Current Limits v2 only

Set the spa's maximum current (vmax) and current limit (clmt) in amps. These should match your electrical supply.

  • Topic: <baseTopic>/set/vmax and <baseTopic>/set/clmt
  • Payload: An integer number of amps

WARNING

Only change these if you understand your spa's electrical installation. Setting them incorrectly can trip breakers or damage equipment.

Keypad Up v2 only

Simulate an Up key press on the spa's keypad. Most commonly used to wake the spa from sleep so that it responds to other commands.

  • Topic: <baseTopic>/set/keypad_up
  • Payload: Any value (the payload is ignored)

Availability Topic

Indicates whether the spa controller is online (connected to MQTT) or offline. Useful for monitoring device health, automation triggers, and ensuring your spa is reachable for remote control.

  • Topic: <baseTopic>/available
  • Payload: online or offline
  • Notes: Both values are published retained, so a client that connects later immediately sees the spa's current state rather than waiting for the next update. The topic is also registered as the MQTT Last Will and Testament, so the broker publishes offline automatically if the controller drops off the network.

WARNING

Because the availability topic is retained and the base topic changed in v2, a stale retained offline message can be left behind on the old v1 topic. See Topic Structure by Firmware Version for how to clear it.

Home Assistant Discovery Topics

If Home Assistant auto-discovery is enabled, the controller also publishes retained configuration payloads under the homeassistant/ prefix:

homeassistant/<component>/<spaSerialNumber>/<spaSerialNumber>-<propertyId>/config
  • <component> is the Home Assistant entity type, such as sensor, switch, light, fan, climate, select, number, text, button, or binary_sensor.
  • <spaSerialNumber> is the spa's serial number (SerialNo1-SerialNo2), for example 24483862-24490698.

WARNING

Discovery topics still use the spa serial number, even in v2. Only the data topics moved to the eSpa/<deviceId>/ base topic. Don't assume the two identifiers are the same.

The light entity is published with brightness_scale: 5, matching the 15 brightness range described above.

Each discovery payload points back at <baseTopic>/status for state, <baseTopic>/set/<property> for commands, and <baseTopic>/available for availability, so these entities follow exactly the same topics documented above.