Home Assistant custom integration for a Balboa-compatible spa pool connected through an Elfin Wi-Fi RS-485-to-TCP adapter.
The integration communicates directly with the spa controller over TCP on the local network. It does not require a cloud service, the identification/configuration handshake used by the official Balboa Wi-Fi module, or any intermediary software such as a Ruby proxy or MQTT broker.
Warning
This project has been developed and tested against a specific Balboa-compatible spa installation. Balboa controller configurations and message layouts vary. Confirm operation carefully before relying on it for unattended control.
- Local, asynchronous TCP communication with the spa controller
- Home Assistant UI configuration flow
- Automatic reconnection after network or bridge interruptions
- Current and target water temperature
- Heating state and heat mode
- Ready, Rest and Ready-in-Rest control
- High and low temperature-range control
- Spa clock display and clock synchronisation
- Filter-cycle, lock, circulation-pump and controller-state sensors
- Maintenance reminder/notification decoding and clearing
- Fault-log retrieval through a Home Assistant event entity
- Stateless controls for pumps, blowers and lights
- Diagnostic entities for protocol investigation
- Downloadable Home Assistant diagnostics with connection and protocol statistics
The expected topology is:
Home Assistant
|
Wi-Fi/LAN
|
Elfin RS-485-to-TCP adapter
|
RS-485
|
Balboa-compatible spa controller
The integration was developed with a transparent Elfin-style adapter, such as an Elfin EW11, replacing or supplementing the official Balboa Wi-Fi interface.
Typical bridge settings are:
| Setting | Value |
|---|---|
| Mode | Transparent TCP server |
| TCP port | 4257 |
| Serial baud rate | 115200 |
| Data bits | 8 |
| Parity | None |
| Stop bits | 1 |
| Flow control | None |
The exact Elfin configuration interface varies by model and firmware. The adapter must expose the raw spa bus as an unmodified TCP byte stream.
Caution
The spa controller is mains-powered equipment. Use an appropriately isolated RS-485 interface and follow the controller and adapter manufacturers' electrical requirements. Do not work on the spa wiring while it is energised.
-
Copy the integration directory into Home Assistant:
/config/custom_components/spa_pool/ -
Confirm that the directory contains at least:
custom_components/ └── spa_pool/ ├── __init__.py ├── binary_sensor.py ├── button.py ├── climate.py ├── client.py ├── config_flow.py ├── const.py ├── diagnostics.py ├── event.py ├── manifest.json ├── models.py ├── protocol.py ├── sensor.py ├── strings.json └── translations/ └── en.json -
Restart Home Assistant.
-
Go to Settings → Devices & services → Add integration.
-
Search for Spa Pool.
-
Enter the Elfin adapter's IP address or hostname and TCP port. The default port is
4257.
The setup flow verifies that the bridge can be reached and that at least one valid spa frame can be received.
The repository includes HACS metadata. Once the project has been published on GitHub:
- Open HACS → Integrations.
- Select the three-dot menu and choose Custom repositories.
- Add the GitHub repository URL as an Integration.
- Install Spa Pool and restart Home Assistant.
- Add the integration from Settings → Devices & services.
The exact entity set depends on the integration version and the configured spa capabilities.
The main climate entity provides:
- Current water temperature
- Target temperature
- Heating state
- Heat mode/preset selection
- Temperature-range selection
Example service calls:
action: climate.set_temperature
target:
entity_id: climate.spa_pool
data:
temperature: 38action: climate.set_preset_mode
target:
entity_id: climate.spa_pool
data:
preset_mode: readyPumps, blowers and lights are controlled using stateless next state or next mode buttons.
A button press is equivalent to pressing the corresponding physical spa-panel button. Depending on the installed accessory, repeated presses may cycle through states such as:
Off → Low → High → Off
or:
Off → Mode 1 → Mode 2 → … → Off
This approach avoids falsely asserting an accessory state when the controller's status-byte mapping has not been verified for a particular spa configuration.
Example:
action: button.press
target:
entity_id: button.spa_pool_pump_2_next_stateAvailable entities include operational and diagnostic information such as:
- Current and target temperature
- Spa time
- Operational state
- Initialisation/controller-notification state
- Heat mode and temperature range
- Heating active
- Filter-cycle state
- Circulation pump active
- Panel and settings locks
- Maintenance notification
- Status-stream availability
- Raw or last-valid protocol messages
Some protocol-oriented entities are disabled by default and can be enabled from the integration's entity list.
Management and diagnostic actions include:
- Restart stream
- Synchronise clock
- Refresh fault log
- Refresh device configuration
- Clear controller notification
- Advance a pump, blower or light to its next state
The fault-log event entity reports decoded controller fault information when a fault entry is received. Event attributes can include the message code, description, severity, controller time and age of the stored entry.
Example automation:
alias: Spa fault notification
triggers:
- trigger: state
entity_id: event.spa_pool_fault_log
conditions:
- condition: template
value_template: >
{{ trigger.to_state is not none
and trigger.to_state.attributes.get('message_code', 0) | int(0) != 0 }}
actions:
- action: persistent_notification.create
data:
title: Spa fault
message: >
{{ trigger.to_state.attributes.get(
'description', 'Unknown spa fault'
) }}
mode: queuedBalboa-compatible messages use 0x7E frame delimiters and a one-byte checksum. The integration:
- Reassembles frames from arbitrary TCP chunks.
- Validates declared lengths and checksums.
- Decodes supported status, configuration, maintenance and fault messages.
- Preserves unknown values rather than terminating the stream.
- Serialises outgoing commands so that integration commands do not interleave.
- Retains raw diagnostic data for investigation of unsupported controllers.
The bridge is treated as a transparent transport. The integration does not assume that it emulates all behaviour of an official Balboa Wi-Fi module.
Check that:
- The Elfin adapter has a stable IP address or DHCP reservation.
- TCP port
4257is reachable from Home Assistant. - The adapter is operating as a transparent TCP server.
- Serial settings match the spa bus.
- Another application is not occupying the bridge's only permitted TCP connection.
- Home Assistant can receive continuous spa traffic after connecting.
Verify the serial wiring, polarity and bridge settings. A TCP connection alone does not prove that valid RS-485 data is reaching Home Assistant.
Enable the diagnostic entities and inspect the integration logs:
logger:
default: warning
logs:
custom_components.spa_pool: debugRestart Home Assistant after changing logger configuration.
Accessory numbering and state encoding can differ between controller configurations. Use the corresponding next state button cautiously and compare each press with the physical control panel and the raw status frame.
Go to:
Settings → Devices & services → Spa Pool → three-dot menu → Download diagnostics
Diagnostics include connection state, parser statistics, decoded controller state and recent protocol information. Review diagnostics before sharing them, even though Home Assistant's diagnostics framework is intended to redact configured secrets.
This is an independently developed custom integration and is not an official Home Assistant or Balboa Water Group project.
The protocol has been reverse engineered from observed traffic and publicly available community implementations. Not every Balboa controller, topside panel, accessory configuration or firmware version is expected to behave identically.
Useful contributions include:
- Packet captures paired with a description of the physical panel action
- Controller and topside-panel model information
- Previously unseen status or fault messages
- Tests for protocol parsing and command generation
- Confirmed accessory-state mappings for additional spa configurations
Please do not include public IP addresses, Wi-Fi credentials or other private network information in issues or captures.
This project was developed independently but benefited from the protocol research, documentation, and prior work of the Home Assistant and Balboa communities, particularly the following projects:
- ccutrer/balboa_worldwide_app – extensive reverse engineering of the Balboa spa protocol, including protocol documentation and RS-485/TCP behaviour.
- garbled1/pybalboa – asynchronous Python library for communicating with Balboa spa controllers, used by several other community projects.
- garbled1/balboa_homeassistan – Home Assistant integration designed for use with the official Balboa Wi-Fi module.
- jshank/bwalink – demonstrates communication with Balboa controllers via generic RS-485-to-TCP adapters, including the Elfin EW11.
- Home Assistant Core – integration architecture, entity-platform patterns, and developer APIs.
- HACS – framework and validation requirements for distributing Home Assistant custom integrations.
Unlike many existing solutions, Spa Pool communicates directly with the spa controller over a transparent TCP-to-RS-485 adapter. It does not require the official Balboa Wi-Fi module, a cloud service, a Ruby proxy, an MQTT broker, or any other intermediary software.
These projects are acknowledged as valuable references and prior art but are not runtime dependencies of this integration.
Licensed under the Apache License 2.0. See LICENSE.