How to add a Tuya based pool heat pump to OpenHAB

EDIT: code refactor to make it easier to read / follow

I’ve been testing out my JRuby rule to automate my pool heater so it only uses power from my PV / solar generated output, and not draw from the grid. I have a Fronius inverter + smart meter and using the Fronius binding, I can get the grid import (positive number) / export (negative) data.

I also control my Chlorinator timing / power in another rule, based on the sun elevation which changes according to the season. The pool pump is connected to a “logic or” circuit, so it will turn on when at least one of the chlorinator or the heat pump is on.

Here’s my current rule to control the pool heat pump:

require 'openhab'

TARGET_TEMPERATURE = QuantityType.new('30 °C')
SET_TEMPERATURE = QuantityType.new('36 °C')

#
# Run the pool heater on a strict time based schedule
# Set the Target Temperature to normal
# Turn on the heater earlier and keep it on if there's plenty of solar power outside the schedule
#
def run_scheduled_heating
  PoolHeater_Set_Temp.ensure << TARGET_TEMPERATURE
  PoolHeater_Boost.ensure.on
  if @avg_grid_export > '3kW'
    PoolHeater_Power.ensure.on
  else
    case TimeOfDay.now
    when '9am'..'3pm' then PoolHeater_Power.ensure.on
    when ('4pm'..) then PoolHeater_Power.ensure.off
    end
  end
end

def update_power_stats
  @avg_solar_power = Solar_AC_Power.average_since(15.minutes, :influxdb) || QuantityType.new("0 W")
  @avg_grid_draw = Solar_Grid_Power.average_since(1.minutes, :influxdb)
  @avg_grid_draw60 = Solar_Grid_Power.average_since(60.minutes, :influxdb)
  @avg_grid_export = @avg_grid_draw.negate
end

#
# Decide whether the heater needs to be turned on
#
# @return [Boolean] true if the heater is on, false otherwise
#
def power_on
  return true if PoolHeater_Power.on?
  return false unless TimeOfDay.now.between?('7am'..'3pm')
  return false unless @avg_solar_power.positive?
  return false unless @avg_grid_export > '3 kW' || Pool_Chlorinator.on?

  PoolHeater_Power.ensure.on
  true
end

#
# Send an OFF command when necessary, but always returns true
#
# @return [Boolean] Always returns true
#
def turn_off_heater
  PoolHeater_Power.ensure.off
  true
end

#
# Decide whether the heater needs to be turned off
#
# @return [Boolean] true if the heater is turned off, false/nil otherwise
#
def power_off
  return turn_off_heater if TimeOfDay.now >= '6pm'
  return turn_off_heater if TimeOfDay.now > '4pm' && @avg_grid_draw > '1 kW'
  return turn_off_heater unless @avg_solar_power.positive?
  return turn_off_heater if @avg_grid_draw60 && @avg_grid_draw60 > '2 kW'
end

#
# Adaptively return the target temperature.
# Lower the target temp on a day with low solar production
#
# @return [QuantityType] The adjusted target temperature
#
def target_temperature
  return TARGET_TEMPERATURE - '2 °C' if @avg_grid_draw60.nil? || @avg_grid_draw60 > '2 kW' || @avg_solar_power < '1 kW'

  TARGET_TEMPERATURE
end

#
# Returns true if the target temp has been reached. Also adjust the set temp
#
# @return [Boolean] true if the pool's current temp is the same or more than the target temp
#
def target_temp_reached?
  result = PoolHeater_Current_Temp >= target_temperature
  PoolHeater_Set_Temp.ensure << (result ? target_temperature : SET_TEMPERATURE)
  result
end

#
# Adjust the heater's Boost mode on/off depending on excess solar power
#
def power_saving_mode
  return unless @avg_grid_draw
  return if target_temp_reached?

  if PoolHeater_Boost.on?
    PoolHeater_Boost.off if @avg_grid_draw > '2 kW'
  elsif @avg_grid_export > '1 kW'
    PoolHeater_Boost.on
  end
end

rule 'Pool: Heater Control' do
  every 5.minutes
  between '9am'..'7pm'
  on_start
  run do
    update_power_stats
    if PoolHeater_Power_Saving.on?
      power_saving_mode if power_on && !power_off
    else
      run_scheduled_heating
    end
  end
end

To plot the pool temperature, I only persist the data when the pool heater is on. When it’s off overnight, the water isn’t circulating and it will only sense the temperature of the water inside the unit. So I use this rule:

rule 'Pool: Persist pool temp' do
  changed PoolHeater_Current_Temp
  only_if PoolHeater_Power
  run { PoolHeater_Current_Temp.persist(:influxdb) }
end
1 Like