Here’s how I was able to get my hot tub (Divine Sinclair) to show up in Home Assistant to allow all of the automation goodness. Clearwater Spas/Balboa equipment speaks a proprietary protocol over RS-485. Luckily for us, it’s been decoded and put into an open source project that uses MQTT as a communications broker.
UPDATE 2 You no longer need to manually create the entities. Balboa Worldwide App MQTT Homie Bridge now publishes them through Home Assistant MQTT Auto Discovery!
UPDATE 1 I’ve tested and confirmed the $24 RS-485 to WiFi works quite well and migrated the software to a docker container. Check out jshank/bwalink on GitHub for more details.
Original Post
This is version 1.0 and very, very DIY. For V2, I’m using an ESP8266 to just relay the RS-485 data over wifi to another host for processing to MQTT (still a bit janky). V3 looks to be a $24 premade RS-485 to WiFi adapter but I still need to test it. You could also spring for the $250 Balboa wifi module that does the exact same RS485 over WiFi, but what fun would that be.
Disclaimers and Legal Stuff First
As an Amazon Associate I earn from qualifying purchases. I get commissions for purchases made through links in this post. Most hot tubs are fed 240v and are capable of delivering 50-amps of current = 12,000 W so… electricity is dangerous and can cause personal injury or DEATH as well as other property loss or damage if not used or constructed properly. If you have any doubts whatsoever about performing do-it-yourself electrical work, PLEASE do the smart thing and hire a QUALIFIED SPECIALIST to perform the work for you.
NEVER WORK WITH LIVE VOLTAGE. Always disconnect the power source before working with electrical circuits.
How to Get Your Hot Tub into Home Assistant
Parts
- ATX Molex Micro Fit Connector
- DC-DC Buck Converter
- Silicone wire optional if you already have wire but much easier to work with
- Micro-USB Pigtails
- Heat Shrink Tubing or Electrical Tape
- 3d printed enclosure for the buck converter
- Raspberry Pi 3B or one with a wifi adapter + case
- RS-485 USB Adapter
Tools
- Soldering Iron and solder
- Good wire strippers/cutters
- Multimeter
1. Make the cable
My hot tub had a small Y-Cable hanging outside of the control box that you can plug into for power and to get to the RS-485 communications of the tub. If you don’t have this cable, you can likely plug into the main control board IF it has the same outlet type. This should be the same type of connector that goes to your control panel.
Note that the tab is on the left of the connector
The connection is an ATX Molex Micro Fit Connector 4Pin (no, not the same as your old ATX power supply, I know, I tried too). Cut the one you ordered in half since you’ll be making your own cable.
Next, we are going to adjust that 15.9 Volt output to 5 Volts so we can power our Raspberry Pi. I used a 12V power supply to calibrate but anything with more than 9V DC should work. Connect your multimeter and power supply to the DC-DC buck converter as shown below. Set your multimeter to DC voltage and then, using a small screwdriver, carefully turn the adjustment screw until the multimeter reads 5V.
These things are easy to break which is why then send you a bunch.
Disconnect everything from the DC-DC buck converter and be careful not to touch that adjustment screw. After final testing, put a small drop of hot glue on it to prevent accidental adjustments.
From the ATX Molex Micro Fit Connector, strip and solder the wire corresponding to +15.9 V DC to the DC-DC buck converter IN+ and the wire corresponding to ground to the DC-DC buck converter IN-. Then solder a length of the black silicone coated wire to the DC-DC buck converter OUT- and a length of the red silicone coated wire to the DC-DC buck converter OUT+. I suggest about 36" so you can stash the Raspberry Pi in the media-device pocket. Don’t forget to heat-shrink or electrical tape those connections.
Next we’re going to test our work and make sure that we did everything right. Strip the ends of the other ATX Molex Micro Fit Connector and take it, your multimeter, and your partially completed cable, out to the tub. Put some electrical tape around the exposed cables corresponding to the RS-485 A and B connections so you don’t short them. Connect your multimeter to the stripped connections (wire corresponding to +15.9 V DC and Ground) and make sure you are getting voltage where you expect it.
Now connect up your partially completed cable and measure the voltage coming from the DC-DC buck converter to confirm you are getting 5V, adjust the DC-DC buck converter screw if needed. If everything checks out, disconnect your partially completed cable and head back to your workbench/desk/table.
Heat that soldering iron back up and don’t forget to slide heat shrink tubing on the red and black silicone wires coming from the DC-DC buck converter. Solder those wires to the Micro-USB pigtail (red to red and black to black) and then seal everything up nicely with heat shrink or electrical tape. Insert the DC-DC adapter into your 3D printed enclosure or wrap it up with more electrical tape.
For the data wires, cut and solder a length of blue silicone coated wire to the ATX Molex Micro Fit Connector wire corresponding to the RS-485 B or -. Do the same for with a yellow length for the RS-485 A or +. I suggest crimping some 22AWG ferrules to the ends of those cables or you can just tin them and be careful.
That was the hardest part of the project, congratulate yourself.
Finished Cable
2. Setup the software
- Setup an MQTT Broker
- Following the directions here, install the Raspberry PI OS (formerly Raspbian) on your Raspberry PI SD card and get it connected to your wireless network
NOTE: You can just run jshank/bwalink in Docker if you don’t want to install Ruby and Balboa Worldwide App MQTT Homie Bridge. jshank/bwalink even supports a remote connection to a serial over IP device. More info in the GitHub readme.
- SSH to your Raspberry Pi and
sudo apt install ruby
- Install the Balboa Worldwide App MQTT Homie Bridge by following the directions in the link. Note that your RS-485 adapter will most likely be
/dev/ttyUSB0
if you bought the recommended hardware from the parts list. - Subscribe to the
homie/#
topic on your MQTT broker and then start the BWA servicesudo systemctl start bwa_mqtt_bridge
to confirm you are getting data - Shutdown the Raspberry Pi and head out to the tub with your Pi, RS485 USB adapter and custom cable.
- Route the cable however you see fit and screw the blue wire into the B- terminal and yellow wire into the A+ terminal of the RS-485 to USB adapter. Plug the other end of your customer cable into the tubs Y-cable or J33 port on the main control board
- Plug the micro-USB connector into your Pi and marvel at your work so far.
Within a few minutes and with some luck, you’ll see MQTT populated with data from your tub (example below).
homie/bwa/$homie 4.0.0
homie/bwa/$name BWA Spa
homie/bwa/$state ready
homie/bwa/$nodes spa
homie/bwa/spa/$name BWA Spa
homie/bwa/spa/$type CL501X1
homie/bwa/spa/$properties priming,heatingmode,temperaturescale,24htime,heating,temperaturerange,currenttemperature,settemperature,filter1,filter2,pump1,pump2,pump3,light1,circpump
homie/bwa/spa/priming true
homie/bwa/spa/priming/$name Is the pump priming
homie/bwa/spa/priming/$datatype boolean
homie/bwa/spa/heatingmode ready
3. Configure Home Assistant
NOTE: This is no longer necessary since Balboa Worldwide App MQTT Homie Bridge publishes the entities in the BWA Link device using MQTT Auto Discovery.
You can now setup Home Assistant using the following yaml configurations that represent the sensors and switches of your tubs functions.
Switches
- platform: mqtt
name: "Hot Tub 24hour Clock Mode"
state_topic: "homie/bwa/spa/24htime"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
command_topic: "homie/bwa/spa/24htime/set"
payload_on: "true"
payload_off: "false"
icon: mdi:wrench-clock
- platform: mqtt
name: "Hot Tub Pump 1 (Lounge)"
state_topic: "homie/bwa/spa/pump1"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
command_topic: "homie/bwa/spa/pump1/set"
payload_on: "toggle"
payload_off: "toggle"
state_on: 2
state_off: 0
icon: mdi:chart-bubble
- platform: mqtt
name: "Hot Tub Pump 2 (Seats)"
state_topic: "homie/bwa/spa/pump2"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
command_topic: "homie/bwa/spa/pump2/set"
payload_on: "toggle"
payload_off: "toggle"
state_on: 2
state_off: 0
icon: mdi:chart-bubble
- platform: mqtt
name: "Hot Tub Pump 3 (Feet)"
state_topic: "homie/bwa/spa/pump3"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
command_topic: "homie/bwa/spa/pump3/set"
payload_on: "toggle"
payload_off: "toggle"
state_on: 2
state_off: 0
icon: mdi:chart-bubble
- platform: mqtt
name: "Hot Tub Light"
state_topic: "homie/bwa/spa/light1"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
command_topic: "homie/bwa/spa/light1/set"
payload_on: "true"
payload_off: "false"
state_on: "true"
state_off: "false"
icon: mdi:car-parking-lights
Sensors
- platform: mqtt
name: "Hot Tub Circulation Pump"
state_topic: "homie/bwa/spa/circpump"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:sync
- platform: mqtt
name: "Hot Tub Priming"
state_topic: "homie/bwa/spa/priming"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
- platform: mqtt
name: "Hot Tub Temperature Scale"
state_topic: "homie/bwa/spa/temperaturescale"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
- platform: mqtt
name: "Hot Tub Heating"
state_topic: "homie/bwa/spa/heating"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:hot-tub
- platform: mqtt
name: "Hot Tub Temperature Range"
state_topic: "homie/bwa/spa/temperaturerange"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:thermometer-lines
- platform: mqtt
name: "Hot Tub Current Temperature"
state_topic: "homie/bwa/spa/currenttemperature"
unit_of_measurement: "°F"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
- platform: mqtt
name: "Hot Tub Filter 1 Cycle Running"
state_topic: "homie/bwa/spa/filter1"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:air-filter
- platform: mqtt
name: "Hot Tub Filter 2 Cycle Running"
state_topic: "homie/bwa/spa/filter2"
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:air-filter
Numbers
- platform: mqtt
unique_id : hot_tub_set_temp
name: "Hot Tub Set Temperature"
state_topic: "homie/bwa/spa/settemperature"
command_topic: "homie/bwa/spa/settemperature/set"
min: 80
max: 104
availability:
- topic: "homie/bwa/$state"
payload_available: "ready"
payload_not_available: "lost"
icon: mdi:thermometer
Input Select
input_select:
hottub_mode:
name: Hot Tub Mode
options:
- ready
- rest
- ready_in_rest
hottub_temperature_range:
name: Hot Tub Temperature Range
options:
- high
- low
Automations to support the selectors
- alias: "Update Spa Heating Mode Selector"
trigger:
platform: mqtt
topic: "homie/bwa/spa/heatingmode"
# entity_id: input_select.thermostat_mode
action:
service: input_select.select_option
target:
entity_id: input_select.hottub_mode
data:
option: "{{ trigger.payload }}"
- alias: "Set Hot Tub Heating Mode"
trigger:
platform: state
entity_id: input_select.hottub_mode
action:
service: mqtt.publish
data:
topic: "homie/bwa/spa/heatingmode/set"
payload: "{{ states('input_select.hottub_mode') }}"
- alias: "Update Hot Tub Temperature Range Selector"
trigger:
platform: mqtt
topic: "homie/bwa/spa/temperaturerange"
# entity_id: input_select.thermostat_mode
action:
service: input_select.select_option
target:
entity_id: input_select.hottub_temperature_range
data:
option: "{{ trigger.payload }}"
- alias: "Set Hot Tub Temperature Range"
trigger:
platform: state
entity_id: input_select.hottub_temperature_range
action:
service: mqtt.publish
data:
topic: "homie/bwa/spa/temperaturerange/set"
payload: "{{ states('input_select.hottub_temperature_range') }}"
Automations to heat the tub when I have excess solar and PG&E isn’t charging an arm and a leg per kWH
- alias: "Set Hot Tub Temperature Ready"
trigger:
platform: time
at: "11:00:00"
condition:
condition: state
entity_id: group.family
state: "home"
action:
service: mqtt.publish
data:
topic: "homie/bwa/spa/settemperature/set"
payload: "99"
- alias: "Set Hot Tub Temperature Standby"
trigger:
platform: time
at: "17:00:00"
action:
service: mqtt.publish
data:
topic: "homie/bwa/spa/settemperature/set"
payload: "80"