esphome / issues

Issue Tracker for ESPHome
https://esphome.io/
290 stars 35 forks source link

Bang Bang Climate idle action not being triggered #505

Closed l1c2d3 closed 5 years ago

l1c2d3 commented 5 years ago

Operating environment/Installation (Hass.io/Docker/pip/etc.):

Hass.io ESP (ESP32/ESP8266, Board/Sonoff):

Lolin32 with integrated OLED Affected component:

https://esphome.io/components/climate/bang_bang.html

Description of problem: I'm currently testing a thermostat build (not yet connected to my HVAC system), with an ESP32 with integrated OLED, SHT31 Temperature sensor, and 3 relays (Fan, Cool, Heat).

When I set the thermostat to Cool, Fan and Cool relays activate. When the temperature is within range, the idle action is set to turn the cool relay off, but this never happens.

In Auto mode it's working properly, when the temp condition is met, fan (with an automation) and cool relays turn off.

I would also like to know if idle is shown as state, this to create an automation to delay 5 min turning on the compressor after it changes from idle to cool.

Thank you in advance for your help.

Problem-relevant YAML-configuration entries:

climate:
  - platform: bang_bang
    id: therm_test
    name: "Thermostat Test"
    sensor: sht_temp
    default_target_temperature_low: 13 °C
    default_target_temperature_high: 24 °C
    cool_action:
      - switch.turn_on: fan
      - switch.turn_on: cool
    idle_action:
      - switch.turn_off: cool
      - switch.turn_off: heat
    heat_action:
      - switch.turn_on: fan
      - switch.turn_on: heat
    visual:
      min_temperature: 10 °C
      max_temperature: 32 °C
      temperature_step: .5

Logs (if applicable):

[13:06:03][D][sht3xd:059]: Got temperature=21.40°C humidity=44.85%
[13:06:03][D][sensor:092]: 'SHT Temperature': Sending state 21.39529 °C with 1 decimals of accuracy
[13:06:03][D][climate:172]: 'Thermostat Test' - Sending state:
[13:06:03][D][climate:175]:   Mode: COOL
[13:06:03][D][climate:177]:   Current Temperature: 21.40°C
[13:06:03][D][climate:181]:   Target Temperature: Low: 14.00°C High: 24.00°C

Additional information and things you've tried:

OttoWinter commented 5 years ago

COOL mode means you manually force the device into cooling mode - no automatic setting is active.

That's what auto mode is for: as the name implies, that includes the automatic heat/cool/idle setting.

l1c2d3 commented 5 years ago

Hi Otto, thank you for taking the time to respond.

What I'm looking for is to emulate the operation of current thermostat, which is how every commercial thermostat operates, at least in the US, Mexico and Canada. My HVAC is a 5 ton Rheem with Heat Pump, my current thermostat is a non-programmable Honeywell, which has a 5 minutes protection timer to prevent compressor damage when restarting quickly.

The operation is the following:

Auto - Cool: Turns on fan and compressor, cools until desired temperature, then turns off fan and compressor. It waits for the temperature to raise and waits at least 5 minutes to turn back on the fan and compressor (protection feature). Fan On - Cool: Turns on fan and compressor, cools until desired temperature, then the compressor turns off (Idle). Fan is kept running. It waits until the temperature raises and turns the compressor back on (same protection time applies). Fan Auto - Heat: Same as Auto - Cool. Fan On - Heat: Same as On - Cool. Idle: Compressor is off but fan is running. Fan On - OFF: The Fan turns on indefinitely. Off - Off: HVAC is off.

In your Cool/Heat mode approach, the compressor will be running indefinitely, thus cooling past the target temperature setting, which may cause damage to the compressor and reduce its life, and will also increase the electricity bill. Only using Auto, could be programmed to be used as intended, as you say, taking the heat and cool target values and operate accordingly, which is nice. But it leaves the other 2 modes useless, at least for a setup like mine.

Workaround

I Used lambdas, in the sht sensor on_value trigger, as a workaround to emulate the cooling and heating modes, including compressor protection (time before the compressor turns back on after idling, in this case 5 minutes or 300s). Also I used the same hysteresis as in my thermostat which is around 1 degree C. I used a variable to set the state manually, since I couldn't get the mode from the component itself. So far, is working as intended, will test for a few more days, then I will replace my Honeywell.

I will leave here my workaround if anyone else has the same need.

globals:
  - id: therm_mode
    type: int
    initial_value: '0' #off, 1 - auto, 2 - cool, 3 - heat
  - id: timer
    type: long unsigned int
    initial_value: '0'
    restore_value: yes

climate:
  - platform: bang_bang
    id: therm_test
    name: "Thermostat Test"
    sensor: sht_temp
    default_target_temperature_low: 13 °C
    default_target_temperature_high: 24 °C
    cool_action:
      - lambda: !lambda |-
          id(fan).turn_on();
          if(id(therm_mode) != 2) //coming from other modes
          {
            if(id(therm_mode) != 1)//check if it's not in Auto mode, then will change the mode variable to cool
            {
              id(therm_mode) = 2;
            }
            if(id(sht_temp).state >= id(therm_test).target_temperature_high+1.0) //current temp against target temp + 1
            {
              if(id(timer) == 0 || id(clk).now().timestamp >= id(timer)+300) //if the protection time has passed will turn on the compressor, or if the hvac was off (timer = 0)
              {
                id(cool).turn_on();
                id(timer) = 0;
              }
            }  
          }
    idle_action:
      - lambda: !lambda |-
          id(therm_mode) = 1; //set mode variable to auto
      - switch.turn_off: fan
      - switch.turn_off: cool
      - switch.turn_off: heat
      - lambda: !lambda |-
          id(timer) = id(clk).now().timestamp; //starts protection
    heat_action:
      - lambda: !lambda |-
          id(fan).turn_on();
          if(id(therm_mode) != 3)
          {
            if(id(therm_mode) != 1)
            {
              id(therm_mode) = 3;
            }
            if(id(sht_temp).state <= id(therm_test).target_temperature_low-1.0)
            {
              if(id(timer) == 0 || id(clk).now().timestamp >= id(timer)+300)
              {
                id(heat).turn_on();
                id(timer) = 0;
              }
            } 
          }
    visual:
      min_temperature: 10 °C
      max_temperature: 32 °C
      temperature_step: .5

  - platform: sht3xd
    temperature:
      name: "SHT Temperature"
      id: sht_temp
      unit_of_measurement: "°C"
      on_value: #Turn off compressor while in Cool or Heat Mode after desired temp is reached
        then:
          - lambda: !lambda |-
              if(id(therm_mode) == 2) //if mode is cool
              {
                if(x >= id(therm_test).target_temperature_high+1.0) //checks current temp against cooling target + 1
                {
                  if(id(timer) == 0 || id(clk).now().timestamp >= id(timer)+300) //checks protection
                  {
                    id(cool).turn_on();
                    id(timer) = 0; //resets protection mode
                  }
                }
                else if(x <= id(therm_test).target_temperature_high-1.0) //idle for cooling mode. checks current temp against target temp -1, then compressor turns off
                {
                  id(cool).turn_off();
                  id(timer) = id(clk).now().timestamp; //sets protection mode
                }
              }
              if(id(therm_mode) == 3) //same for heat
              {
                if(x <= id(therm_test).target_temperature_low-1.0)
                {
                  if(id(timer) == 0 || id(clk).now().timestamp >= id(timer)+300)
                  {
                    id(heat).turn_on();
                    id(timer) = 0;
                  }
                }
                else if(x >= id(therm_test).target_temperature_low+1.0)
                {
                  id(heat).turn_off();
                  id(timer) = id(clk).now().timestamp;
                }
              }
    humidity:
      name: "SHT Humidity"
      id: sht_hum
    update_interval: 10s
    address: 0x44