3 way mixing valve

The problem is that you want to “open/close” a short time if the temperature is almost right and a longer time if the error is larger! I have found some solution in Nod-red, but problems in that to. Have a solution now in automation short time if error is small and then other automation that open longer time if larger error. But I can’t get a variable setpoint in automation!

This is one solution from nod-red (node-red-contrib-timeprop (node) - Node-RED) but i want to have it in the esp controler if there is some problem with the net :slight_smile:

How about my solution?

floor temp
TherE is floor heating.Left pic is ON/OFF ,middle pic is target temp and right pic is current temp.Graph for 24 hours,so as you can see,the temperature stays in target range.Sorry about my english.temp
and there is target temp low and high

If I understand correctly, yo would ideally want to control valve position continuously between fully open and fully closed, right?

You can do this by using the custom output component and write a lambda function that translates the 0-100% output from the PID climate component to a discrete amount of time the signal to the motor is sent.

This will probably not be 100% accurate, and error will accumulate.

Also, you will need to have a global variable outside the lambda function that keeps track of current motor position.


A more accurate option is to also put a rotary encoder on the motor/valve assembly if possible, and also add that sensor to the ESP to have a better feedback loop

Hi,No,3 way mixing valve does not need to fully open or close,it depends.It mixing together hot water that goes to house from heater and cooled down water,that comes from house and send mixed water(wanted temperature) to house .It may be stays for a month to open only 20%.So my way works perfectly sience december 2022.Heat and cool actions are scripts,that turning on valve for closing or opening with delays.Sory about my english.

Hi, I will try yours. just need to do some google translate :slight_smile:

Thanks for your script, it works for me very well for my mixing valve.

You welcome !

Hi, do you mind posting the “translated” config? :slight_smile:

I’m looking into doing the same except it’s for my hot tub.


I am using the cover component to control my 1-way valve; similar setup. Using a sonoff dual module. Key is to have a run length; my valve closes in 16seconds. The interlock prevents activating both relays simultaneously, causing damage to the actuator.

  - id: action_open
    type: bool
    initial_value: "false" 
  - id: action_close
    type: bool
    initial_value: "false"
  - id: booting
    type: bool
    initial_value: "true"

# GPIO12 Power 1 --> Close
# GPIO5 Power 2 --> Open

  # The switch that turns the UP direction on
  - platform: gpio
    pin: GPIO5
    id: open_pin
    # Use interlocking to keep at most one of the two directions on
    interlock: &interlock_group [open_pin, close_pin]
    # If ESP reboots, do not attempt to restore switch state
    restore_mode: always off

  # The switch that turns the DOWN direction on
  - platform: gpio
    pin: GPIO12
    id: close_pin
    interlock: *interlock_group
    restore_mode: always off
  - platform: restart
    name: "Radiator Kantoor Restart"

  - platform: time_based
    name: "Radiator Kantoor"
    id: radiator_kantoor
    device_class: "damper"
      - switch.turn_off: close_pin
      - switch.turn_on: open_pin
    open_duration: 16sec
    close_duration: 16sec
      - switch.turn_off: open_pin
      - switch.turn_on: close_pin
      - switch.turn_off: open_pin
      - switch.turn_off: close_pin

I am controlling the cover position with a PID controller.
in climate.yaml:

- platform: smart_thermostat
  name: Torenkamer
  unique_id: thermostat_torenkamer
  heater: input_boolean.radiator_kantoor
  target_sensor: sensor.temperatuur_kantoor_ventilatie
  min_temp: 15
  max_temp: 25
  ac_mode: False
  target_temp: 19
    seconds: 60
  away_temp: 15
  kp: 2
  ki: 600
  kd: 300
  sampling_period: 10
1 Like

I control my 3 way mixing valve with a Siemens PLC, function block PID_3Step. Google it.

It has two outputs UP and DN for opening and closing the valve. You configure it with a transition time, min run time and max run time. This will replace the end switches and the position feedback.

With 300 s transition time, a 50% output from the controller will set the UP output for 150 s if starting from 0. I guess that is the principle, next step is to code it

what do you use globals variables for? I don’t see them anywhere in the code

That could have been a leftover of an earlier iteration.

Got my 3-way-mixing valve with the PID Climate platform working by controlling directly two gpios (3-way-mixing valve warmer/cooler) with sigma_delta_output, no cover needed.
The temperature sensor update_interval is in my case 15sec, to get a more stable controller

  - platform: pid
    name: heating_radiator_mixer
    sensor: temperature_radiator_heating_flow
    default_target_temperature: 35°C
    heat_output: sd_radiator_warmer
    cool_output: sd_radiator_cooler
      min_temperature: 25
      max_temperature: 70
      temperature_step: 5
      kp: 0.0025
      ki: 0.0
      kd: 0.5
      output_averaging_samples: 5      # smooth the output over 5 samples
      derivative_averaging_samples: 5  # smooth the derivative value over 10 samples

  - platform: sigma_delta_output
    update_interval: 1s
    id: sd_radiator_warmer
        - homeassistant.service:
            service: switch.turn_on
              entity_id: switch.gpio_radiator_mixer_warmer
        - homeassistant.service:
            service: switch.turn_off
              entity_id: switch.gpio_radiator_mixer_warmer
  - platform: sigma_delta_output
    update_interval: 1s
    id: sd_radiator_cooler
        - homeassistant.service:
            service: switch.turn_on
              entity_id: switch.gpio_radiator_mixer_cooler
        - homeassistant.service:
            service: switch.turn_off
              entity_id: switch.gpio_radiator_mixer_cooler

  - platform: pid     #Sensor just for debug purposes
    name: "PID Climate Radiator Heat Result"
    type: HEAT
    climate_id: heating_radiator_mixer
  - platform: pid     #Sensor just for debug purposes
    name: "PID Climate Radiator Cool Result"
    type: COOL
    climate_id: heating_radiator_mixer