WaterFurnace control via RS-485 bridge with smart control automations

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.

1 Like

@bkprath Hi there! I’m looking at this WaterFurnace. Care to update your experience with HA? It would be a big help!

If you have any power consumption graphics for a heavy day in February and another for day in July this would help me out immensely!

The biggest thing I learned from the WaterFurance integration is that our CC-U02 thermostat is a little flaky. In the summer it’s a little slow to turn the AC on, and then when it does, the thermostat temperature will rise a degree or two while the house is cooling down. Then at some point I guess the house is cool enough so that the thermostat starts showing a lower temperature.

One of the automations as originally posted would drop the heading set point and raise the cooling set point at night. In the winter this worked well, however in the summer I felt it was better to leave the set point alone. In the winter you can adjust at night with blankets, but in the summer on the hottest days, you just end up getting hot. It was interesting to find that most night the AC never comes on even without changing the cooling set point.

This picture shows the thermostat temperature (WaterFurance Zone 1) along with 4 other temperature sensors I have place in the house. The thermostat cooling set point is 79 degrees. You can see the thermostat temperature rising multiple degrees as the other rooms start to cool.

image

Here you have a couple of days of power usage for the Water Furnace AC

Aug 5th and 6th the outside temperature was in the 90s and Aug 7 and 8 in the mid 80s. When the cooling comes on it sucks a lot of power and stays on for a while. Because of the flakey thermostat temperature reading, it over shots and cools the house more that it should. At some point I’ll need to replace the thermostat. As is, power consumption in the summer is typically a few hours during the heat of the day.

Here’s a graph from the winter:

I don’t know what the outside temperature was over this period, but you can see on most days around 5AM the automation kicked the heat back up. Initially I was waiting a little later to kick the heat up, but it took longer than I expected to get to the desired temperature, so I settled on 5AM so things would be good enough by 6:30AM.

I’m not sure about that 5,000 watt spike, but I did notice that the water furnace has some random spikes, like once every two days or so, in the winter, it pulls a bunch of power for a very short period of time. I figured it’s a bug in the water furnace system controller as it didn’t make a lot of sense. Who knows it could be related to the flakey thermostat, but I never figured it out.

Hope this is helpful. If you want some other details let me know.

1 Like

As the outside temperature dropped below freezing this year the house was getting cold inside. The thermostat itself, the CCU-02 model, generates heat, enough to warm the wall behind the thermostat by a few degrees. At first I was concerned that maybe adding the smart house control was somehow having an impact that caused the thermostat to generate this heat. As I though about it, I started thinking when the thermostat was originally installed the installers had set some type of temperature offset. If you hold your finger a about a 1/4 inch from the left top corner for a few seconds a special maintenance menu opens up on this thermostat. If you click the next button a couple of times you’ll find an offset menu that gives you the ability to set a temperature offset. The value was set to zero, I believe this value was reset when the service people upgraded the thermostat firmware to version 3.03. The firmware update was required for the the thermostat to work with the Waterfurnace smart control. I changed the offset to a -3 degrees. With the cooler temperatures this seems to be working great. I’ll have to keep and eye on it when the outside temperature is warmer to see the impact.

The picture below shows two days. The first day which is prior to my adjusting the offset and the second day is with the offset enabled around 10AM. On the first day I had disabled my night time thermostat temperature drop automation because it was way too cold upon getting out of bed in the morning. The second day has the temperature drop automation enabled, but only dropping one degree. You can see how the four Aqara temperature sensors more closely track the purple thermostat temperature once I made the offset adjustment around 10AM.

The temperature drop at night was about sleeping comfort and power savings. I’ll need to watch this as it’s not clear the average power savings is a energy gain with the temperature dropping below 30 degrees outside overnight.