Update below on Jan 31 2025 with current state of automations utilized to control the furnace.
Our house is heated and cooled via a WaterFurance series 7 geothermal heat pump. After a couple of years of using HA I was running out of things to integrate. Then I got the feeling without energy monitoring, the smart home was some how lacking. So I started to integrate energy tracking. If you’re tracking energy you have to track your HVAC system, as it’s probably your biggest energy consumer. Once you start tracking energy usage, you’ll probably want some control capability to make the tracking meaningful. Thus began my effort to smartly integrate the WaterFurance HVAC with HA.
WaterFurance sell the symphony addon capability that can provide cloud based control of your system. There is also an existing HA integration that works with this cloud solution. I’m very opposed to using manufacture cloud solution, which means I don’t have the symphony capability. I looking at possible going the smart thermostat route with energy monitoring clamps for the HVAC. I pulled the thermostat away from the wall and was surprised by the 4 wires on the system. The thermostat wires didn’t appear to be a good match for any of the available smart thermostats. I believe some wiring modifications would have been required to make a smart thermostat work.
Thinks weren’t looking good until I stumbled upon the second post in this thread. The link provided by that post lands on a github site with instructions and code that works with a number of WaterFurance units. The link provides the details on how set up the base capability. I’m creating this thread, as I wasted hours trying to figure out how to control my WaterFurance. Hopefully this thread will make finding this solution easier. I’ve also provide addition details on how to utilize the created device.
The WaterFurnace systems have a maintenance port, which is really just exposes a RS-485 (modbus like) interface. You need a USB device, a modified CAT 5 cable and the software from the link running on some Linux machine. This is all detailed in the instructions found on the github site.
A couple of important things to point out. Your WaterFurance must be running a version of software 3.0 or above. This might require an update both on your HVAC system and on the thermostat. My Waterfurance HVAC controller was running version 2.0 and the Thermostat was running 1.04. With software below version 3.0 the software pulled some information, but it did not provide access to the thermostat controls. You can check the version of software running in your thermostat from the setting screen on thermostat. When the repair guy came out to update the software he gave me the version number running on the HVAC controller.
It wasn’t clear to me from the installation instruction, off the link above, if I’d be able to control the thermostat without the WaterFurance symphony add on. I can report that you don’t need any extra features, you just plug your modified CAT 5 cables into the maintenance RS-485 port.
The provided link has instruction for installing the software. I believe you should be able to install this software directly on your HA controller, assuming you have control at the OS level. I didn’t do that that because I don’t like having non smart home critical capabilities on my HA controller. I installed the capability on a Ubuntu system I have, that provides video services for my home. The software is written in ruby, which means it’s an interpreted language, which means it should work on both x86 and ARM processors.
The install instructions have you install an MQTT broker. As I use the MQTT broker add on available from HA, I did not install the MQTT broker. The line in the install instructions that installs the MQTT broker is:
sudo apt install mosquitto
If you’re using the HA MQTT broker just skip that line.
The instruction have you install a service file at the following location, to control starting the RS-485 to MQTT bridge:
/etc/systemd/system/aurora_mqtt_bridge.service
You will have to modify the ExecStart line in this file so that the service can connects to your MQTT broker. Assuming you’re using the HA MQTT broker the line will be something like this:
ExecStart=/usr/local/bin/aurora_mqtt_bridge /dev/ttyUSB0 mqtt://USER:PASSWORD@IP/
If you don’t have any other USB devices connected to the box running this software then /dev/ttyUSB0 should be the USB device you used to connect to the WaterFurance maintenance port. USER needs to be changed to the USER name you’ve configured for connections to your MQTT broker. Likewise, PASSWORD is the PASSWORD for the MQTT broker user. Finally IP is the IP address of the machine running your MQTT broker.
When you start the aurora_mqtt_bridge service, you should consider using MQTT Explorer, as suggested in the instruction link, to verify all the new topics are created. If that works and you have the HA add on MQTT broker configured correctly, then the thermostat and a lot of other entities should be discovered and placed under one the HA MQTT integration. This is a partial view of what was discovered by HA on my system:
The thermostat is a climate entity and shows in a card that looks like this:
You can use this card to control the HVAC temperature and operational mode.
One items that is reported is the amount of energy currently being used by the HVAC system. You can use this with the HA “Integration - riemann sum integral sensor (helper)” to generate an kWh value. You add the integration and create a new helper using sensor.waterfurnace_heat_pump_total_power_usage as the “input sensor”, k for “Metric prefix” and hours for “time unit”. Most recommendations are to use the “Left Riemann sum” for integration method. I tried both Left and Trapezoidal, and the results were just about identical. The new helper that is created can be placed on the Energy dashboard giving something like this:
This gives you the mean to evaluate if any automation you put in place to adjust the temperature have a positive energy savings effect.
Jan 31st Update.
I stated with three automations to control the furnace and this has now expanded to 7 automation. The original three are still in use. The first automation will lower the heat 1 degree or raise cooling by 2 degrees when everyone is out of the house. I added a vacation mode to this automation that will raise the cooling and lower the heat by 3 degrees. The second automation kicks off the action to get the temperature back to the desired set points as soon as someone returns home.
I use four HA helpers entities in support of these automations. The first is group.persons, which contains all of the individuals living in the house. I track individual’s home and away status with a combination of ping trackers and location information reported by the phone app. The second and third helpers are input_numbers, used to capture the desired low (Heating) and high (Cooling) temperatures. The last helper is a timer used in raise the heating one degree at a time. Here’s the automation that runs when everyone has exited the house:
alias: Thermostat temp change on Away
description: ""
triggers:
- entity_id:
- group.persons
to: not_home
from: home
trigger: state
alias: Change high and low temp when no one home
conditions: []
actions:
- action: timer.cancel
target:
entity_id: timer.aux_heat_timer
data: {}
- if:
- condition: state
entity_id: alarm_control_panel.home_alarm
state: armed_vacation
then:
- metadata: {}
data:
target_temp_high: "{{ states('input_number.high_temp')|float +3 }}"
target_temp_low: "{{ states('input_number.low_temp')|float -3 }}"
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
else:
- metadata: {}
data:
target_temp_high: "{{ states('input_number.high_temp')|float +2 }}"
target_temp_low: "{{ states('input_number.low_temp')|float -1 }}"
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
mode: single
The above automation raises the cooling and lowers the heating thermostat set points. The amount of change is different when the house has been put in vacation mode. I use the HA alarm panel to enable the alarm system. One of the modes on the alarm panel is vacation mode. During vacation mode the change in set points is greater than the daily exiting the house set points.
The next automation handles when a person returns to the house. This automation kicks off the steps to get the house back to the desire heading and cooling set points.
The high and low input number helpers hold the desired values and are used here. The furnace cooling set point is set to the desired level, while the heating set point is simply increased by 1 degree. A timer helper is started to continue the process of raising the temperature in the house.
alias: Thermostat temp change on Home
description: ""
triggers:
- entity_id:
- group.persons
to: home
from: not_home
trigger: state
alias: Change high and low temp when someone returns home
conditions: []
actions:
- metadata: {}
data:
target_temp_low: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low' ) |
float +1 }}
target_temp_high: "{{ states('input_number.high_temp') | float }}"
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
- action: timer.start
target:
entity_id: timer.aux_heat_timer
data:
duration: "00:05:00"
mode: single
The next automation will reduce the temperature by two degrees when everyone should be sleeping and then raise it before people get out of bed. Originally I would also adjust the cooling temperature at night, however I found this wasn’t really desired as we keep the house pretty warm in the summer:
alias: Night Thermostat Heat Shifts
description: ""
triggers:
- at:
- "06:30:00"
- "22:15:00"
trigger: time
conditions:
- condition: state
entity_id: group.persons
state: home
actions:
- choose:
- conditions:
- condition: template
value_template: "{{ trigger.now.hour == 6 }}"
- condition: template
value_template: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low') <
states('input_number.low_temp')|float }}
sequence:
- metadata: {}
data:
target_temp_low: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low'
) | float +1 }}
target_temp_high: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_high'
)|float }}
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
- action: timer.start
target:
entity_id: timer.aux_heat_timer
data:
duration: "01:30:00"
alias: If 6:30 and not at desired heat then increase temp by 1
- conditions:
- condition: template
value_template: "{{ trigger.now.hour == 22 }}"
sequence:
- action: timer.cancel
target:
entity_id: timer.aux_heat_timer
data: {}
- metadata: {}
data:
target_temp_low: "{{ states('input_number.low_temp')|float -2 }}"
target_temp_high: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_high'
)|float }}
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
alias: If 22:15 reduce temperature by 2 degrees
The WaterFurnace will use Aux heat if the temperature difference is greater than 2 degrees. Originally, I would just bump the heating by two degrees in the morning, which should not have kicked off Aux heat. For some strange reason the thermostat ambient temperature will drop a little, even though the house is getting warmer. Every couple of days the aux heat would kick on, which is bad from an energy use perspective. To deal with this I changed the automation to only up the heating 1 degree at a time. I added a timer helper that is used to continue the process of raising the heating temperature until the heating set point gets to the desired level.
The above automation does the initial one degree temperature increase and starts the timer. When the timer finishes the automation below runs and determines if we still need to keep raising the temperature. Before raising the temperature the automation checks the current energy usage of the WaterFurnace to make sure it’s at a good place to raise the temperature another degree. I read that the WaterFurnace is most efficient in the middle of it’s compressors working range. If we haven’t finished raising the temperature to the desired level the timer is started again.
alias: Handle Heating Adjustment Timer
description: ""
triggers:
- event_type: timer.finished
event_data:
entity_id: timer.aux_heat_timer
trigger: event
conditions:
- condition: template
value_template: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low') <
states('input_number.low_temp')|float }}
alias: If temp below desired temp and someone home
- condition: state
entity_id: group.persons
state: home
actions:
- alias: If in a state that will not cause aux heat to kick in then up temp by 1
if:
- condition: template
value_template: >-
{{ states('sensor.waterfurnace_aux_heater_power_usage')|float < 600
}}
- condition: template
value_template: >-
{{ states('sensor.waterfurnace_heat_pump_total_power_usage')|float <
3000 }}
- condition: template
value_template: >-
{{ (state_attr('climate.waterfurnace_zone_1', 'target_temp_low')|float
-1 ) <= states('sensor.waterfurnace_zone_1_ambient_temperature')|float
}}
then:
- metadata: {}
data:
target_temp_high: "{{ states('input_number.high_temp') | float }}"
target_temp_low: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low')| int
+ 1}}
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
enabled: true
- if:
- condition: state
entity_id: person.brian
state: home
then:
- data_template:
message: >-
Heating temp increased by 1 {{
state_attr('climate.waterfurnace_zone_1',
'target_temp_low')|float }}
action: notify.mobile_app_brian_pixel_8
else:
- data_template:
number: XXXXXXXXXX
message: >-
Heating temp increased by 1 {{
state_attr('climate.waterfurnace_zone_1',
'target_temp_low')|float }}
action: shell_command.send_whatsapp_msg
- action: timer.start
target:
entity_id: timer.aux_heat_timer
data:
duration: "00:15:00"
- if:
- condition: state
entity_id: person.brian
state: home
then:
- data_template:
message: "Kicked off heat delay 15 minute timer "
action: notify.mobile_app_brian_pixel_8
else:
- data_template:
number: XXXXXXXXXX
message: "Kicked off heat delay 15 minute timer "
action: shell_command.send_whatsapp_msg
mode: single
My thermostat seems to be a little flaky and arbitrarily reports a drop in ambient temperature for no apparent reason. In some cases the ambient temperature would drops by over 2 degrees, causing the Aux heat to kick on. I added this next automation to look for the aux heat kicking on and when that happened, reduce the temperature set point to turn the aux heat back off. The timer helper mentioned above is used to ensure at some point we get back to the desired heating set point.
alias: Deal with Aux Heating condition
description: ""
triggers:
- alias: When Aux Heat kicks in
trigger: template
value_template: "{{ states('sensor.waterfurnace_aux_heater_power_usage')|float > 1000 }}"
conditions: []
actions:
- choose:
- conditions:
- condition: state
entity_id: group.persons
state: home
- condition: template
value_template: >-
{{ (states('input_number.low_temp')|float - 5) <=
states('sensor.waterfurnace_zone_1_ambient_temperature')|float}}
sequence:
- metadata: {}
data:
target_temp_high: "{{ states('input_number.high_temp') | float }}"
target_temp_low: >-
{{ states('sensor.waterfurnace_zone_1_ambient_temperature')| int
+ 1}}
target:
entity_id: climate.waterfurnace_zone_1
action: climate.set_temperature
enabled: true
alias: Set WF temp to 1 degree above WF ambient_temperature
- if:
- condition: state
entity_id: person.brian
state: home
then:
- data_template:
message: >-
Stopping Aux Heating power {{
states('sensor.emporiavue3_aux_heat_19_power')|float }}
action: notify.mobile_app_brian_pixel_8
else:
- data_template:
number: XXXXXXXXXX
message: >-
Stopping Aux Heating power {{
states('sensor.emporiavue3_aux_heat_19_power')|float }}
action: shell_command.send_whatsapp_msg
alias: Send brian notification
- action: timer.start
target:
entity_id: timer.aux_heat_timer
data:
duration: "00:15:00"
alias: If someone is home and we are within 5 degree of target heat
default:
- alias: Send notification not dealing with Aux heat event
if:
- condition: state
entity_id: person.brian
state: home
then:
- data_template:
message: >-
No one home so ignore Aux Heating power {{
states('sensor.emporiavue3_aux_heat_19_power')|float }}
action: notify.mobile_app_brian_pixel_8
else:
- data_template:
number: XXXXXXXXXX
message: >-
No one home so ignore Aux Heating power {{
states('sensor.emporiavue3_aux_heat_19_power')|float }}
action: shell_command.send_whatsapp_msg
mode: single
The above automation disables the utilization of auxiliary heat as long as the ambient temperature in the house is within 6 degrees of the desired heating set point. This might not be desirable in a really cold environment. In my location I do not expect aux heating to turn on if the WaterFurnace geothermal capability is functional. As result I’m fine with limiting Aux heat ability to raise the house temperature.
The last two automation were added so I could change the heat and cooling set points at the thermostat, and then this would update the two helper values that keep track of the desired high and low temperature set points. These automations will not update the desired set points if one of the above automation is the cause for the change in thermostat set points. The next automation capture a change to the desired heating set point:
alias: Set low temp variable if changed by thermostat
description: ""
triggers:
- trigger: template
value_template: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low')|float !=
states('input_number.low_temp')|float }}
alias: thermostat low temp changed
conditions:
- condition: and
conditions:
- condition: template
value_template: >-
{{ now() - state_attr('automation.night_thermostat_temp_shifts',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.thermostate_temp_change_on_away',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.thermostate_temp_change_on_home',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.deal_with_aux_heating_condition',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.handle_heating_adjustment_timer',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.vacation_done_so_free_thermostat',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.set_thermostat_for_vaction',
'last_triggered') > timedelta(minutes=1) }}
alias: If it wasn't changed by one of the automations
actions:
- metadata: {}
data:
value: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_low' )|float
}}
target:
entity_id: input_number.low_temp
action: input_number.set_value
alias: Save the value to desired low temp variable
mode: single
This final automation here captures a change to the desired the cooling set point.
alias: Set high temp variable if changed by thermostat
description: ""
triggers:
- trigger: template
value_template: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_high')|float !=
states('input_number.high_temp')|float }}
alias: thermostat high temp changed
conditions:
- alias: If it wasn't changed by one of the automations
condition: and
conditions:
- condition: template
value_template: >-
{{ now() - state_attr('automation.night_thermostat_temp_shifts',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.thermostate_temp_change_on_away',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.thermostate_temp_change_on_home',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.deal_with_aux_heating_condition',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.handle_heating_adjustment_timer',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.vacation_done_so_free_thermostat',
'last_triggered') > timedelta(minutes=1) }}
- condition: template
value_template: >-
{{ now() - state_attr('automation.set_thermostat_for_vaction',
'last_triggered') > timedelta(minutes=1) }}
actions:
- metadata: {}
data:
value: >-
{{ state_attr('climate.waterfurnace_zone_1', 'target_temp_high' )|float
}}
target:
entity_id: input_number.high_temp
action: input_number.set_value
alias: Save the value to desired high temp variable
mode: single
The above automation work well. I still have not quantitatively validated that these automation result in a reduction of power utilization. I have done checks on power utilization across a 24 hour period and it appears that power consumption is no worst than if I had just left the thermostat at a fixed set point. The positive is that my wife gets the cooler winter night temperature that she desires for sleeping. I hope in get two 24 hour periods with projected similar temperatures and then run one day with these automation and one day without them. At which point I can compare the total energy usage for these two periods and have a more definitive indication of which mode is better from an energy perspective.