ScratMan / HASmartThermostat

Smart Thermostat with PID controller for HomeAssistant
338 stars 49 forks source link

Features request PWM disabled -> Raw output #25

Closed Buckler89 closed 3 weeks ago

Buckler89 commented 2 years ago

Hi, first of all congratulations for this component. It is very useful to me. In the old version 0.5.1 by not specifying the "pwm" parameter, the output in pwm was disabled in order to have the raw output (0 100%). I understand that it is no longer possible in the latest versions. It's correct? Now the "pwm" has a default right? If i'm wrong please can you explain me how to do it?

In case I am right, i think having this feature could be very usefull. For instace I am controlling the temperature of my guinea pigs house with a shelly dimmer ( https://shelly.cloud/products/shelly-dimmer-2-smart-home-light-controller/ ) that drive an heat light bulb. That doesn't need to be just turned off and on but can keep all the value in the rage 0 - 100%. Imaging also a light bulb turning on and off in the cage like a disco party! Funny yes, but not always :)
Moreover this custom component can be used ( out of the box ) also for other application like illuminance controller (that is in some way what i do) What do you think @ScratMan ?

ScratMan commented 2 years ago

Hello, as far as I understand your application, you are controlling the brightness of a light. If you need the PID output which is a float between 0 and 100, you can create a template sensor that reads the control_output attribute from the thermostat.

buergi commented 2 years ago

Hi, just stumbled across the same problem. I have a gas boiler with an analog input (0-24V, Junkers "1-2-4" interface). I didn't get how the work around with the control_output would help, as I have to specify an entity by using the mandatory "heater" attribute which needs to be a device which provides a turn_on/turn_off service, which I don't have. But one can easily revert to the old version's behavior by just specifying pwm: 0, that way set_control_output uses the async_set method to directly write the control_output to the heater entity.

ScratMan commented 2 years ago

Finally, yes, using a heater entity accepting percent as input and setting pwm: 0 is the solution.

buergi commented 2 years ago

@ScratMan unfortunately, I noticed that, at least for me, my solution is only partially functional. As control entity I'm using an entity using the KNX number platform. At least with this entity, the async_set call does not work, as it replaces the complete state instead of just setting its value attribute. As a cause, the control entity does not forward the control value onto the bus. As a quickfix I replaced the async_set call with async_call like this:

from homeassistant.components.number.const import (
    ATTR_VALUE,
    SERVICE_SET_VALUE,
    DOMAIN as NUMBER_DOMAIN
)

# ...

async def set_control_value(self):
    if self._pwm:
    # ...
    else:
        _LOGGER.info("Change state of %s to %s", self._heater_entity_id,
                     round(self._control_output, 2))
        #self.hass.states.async_set(self._heater_entity_id, self._control_output)
        data = {ATTR_ENTITY_ID: self._heater_entity_id, ATTR_VALUE: self._control_output}
        await self.hass.services.async_call(NUMBER_DOMAIN, SERVICE_SET_VALUE, data)

This way it works for me, however, I'm not sure if there is a more generic way to set a numeric value. If you have got any idea for a proper generic fix, I'm happy to prepare a pull request.

ScratMan commented 2 years ago

I'm still a beginner in Home Assistant development, so I can't tell if there is any other cleaner fix for that. Maybe we can find some examples by looking at the code in other integrations managing lights, covers or valves.

saintman23 commented 2 years ago

I'm trying to use StellaZ valve with this Thermostat, set "pwm : 0" but it seems that the output has decimals, like "44.7"%. HA suggests to use 44 or 45 values. Is it possible to round a number to the nearest integer?

Also StellaZ has maximum valve position at 99, not 100. Can you add maximum valve position setting to fix that?

ScratMan commented 2 years ago

I'm trying to use StellaZ valve with this Thermostat, set "pwm : 0" but it seems that the output has decimals, like "44.7"%. HA suggests to use 44 or 45 values. Is it possible to round a number to the nearest integer?

Also StellaZ has maximum valve position at 99, not 100. Can you add maximum valve position setting to fix that?

I think the best option for you is to keep the PWM and create a template sensor to get the control_output attribute, round it to integer and clamp the range to 0/99.

saintman23 commented 2 years ago

and create a template sensor

I am new to HA, spent couple of hours but could not find any examples of number.sensor template. Could you post here some? Thank you in advance.

saintman23 commented 2 years ago

ended up with usual sensor in configuration.xml:

template:

then using nodered made round(), changed 100 to 99, and pushed the result to HA valve entity. Looks ugly, but now I have the most advanced StellaZ valve with PID thermostat )))

Erelen-Laiquendi commented 2 years ago

You need something like this:

template:
  - sensor:
      - name: "Smart Thermostat Control Output"
        unit_of_measurement: "%"
        state: "{{ (state_attr('climate.smart_thermostat', 'control_output') * 0.99) | round }}"

Or like this to direct send every new value of control output to valve, without template sensor:

- alias: Smart Thermostat to MyValve position
  trigger:
    - platform: state
      entity_id: climate.smart_thermostat
      attribute: control_output
  action:
    - service: set-valve-position-service
      entity_id: my-valve
      data:
        postion: "{{ (state_attr('climate.smart_thermostat', 'control_output') * 0.99) | round }}"
Erelen-Laiquendi commented 2 years ago

@ScratMan, I use raw control_output too. But "heater" parameter is required in smart_thermostat.

I use this dummy switch for it:

switch:
  - platform: template
    switches:
      smart_thermostat_dummy_switch:
        turn_on:
          - delay: 0
        turn_off:
          - delay: 0

Is there another solution? Is it possible to make "heater" optional?

ScratMan commented 2 years ago

@ScratMan, I use raw control_output too. But "heater" parameter is required in smart_thermostat.

I use this dummy switch for it:

switch:
  - platform: template
    switches:
      smart_thermostat_dummy_switch:
        turn_on:
          - delay: 0
        turn_off:
          - delay: 0

Is there another solution? Is it possible to make "heater" optional?

That's the best solution for the moment. This also gives the possibility to extract the control_output from the thermostat and call the service on the valves to set the control_output value as valve opening.

Unfortunately, it looks difficult to make the heater entity optional without breaking things. I'll try to prepare a beta version with @buergi proposal to directly set the valve opening when setting the valve as heater entity with PWM disabled.

AlexeiBaranov commented 2 years ago

That's the best solution for the moment. This also gives the possibility to extract the control_output from the thermostat and call the service on the valves to set the control_output value as valve opening. Unfortunately, it looks difficult to make the heater entity optional without breaking things. I'll try to prepare a beta version with @buergi proposal to directly set the valve opening when setting the valve as heater entity with PWM disabled.

I've created sensor for control_output like this:

template:
  - sensor:
     - name: Heater output
       unique_id: heater_output
       unit_of_measurement: "%"
       state: "{{ state_attr('climate.my_thermostat', 'control_output') }}"

It allows me to use control_output in valve automation. But I had to change set_control_value to isolate control_output changes and heater switch:

    async def set_control_value(self):
        """Set Output value for heater"""
        if self._pwm:
...
        else:
            _LOGGER.info("%s: control_output=%s", self._unique_id,
                            round(self._control_output, 2))
            #self.hass.states.async_set(self._heater_entity_id, self._control_output)
            if self._control_output>0:
                if not self._is_device_active:
                   _LOGGER.info("%s: Request turning on %s", self._unique_id, self._heater_entity_id)
                   await self._async_heater_turn_on()
            else:
                if self._is_device_active:
                   _LOGGER.info("%s: Request turning off %s", self._unique_id, self._heater_entity_id)
                   await self._async_heater_turn_off()

So I can use both - valve and switch. Works nice!

Ex3mXX commented 1 year ago

Is there any progress in this?

buergi commented 1 year ago

Thanks so much for committing this, just updated the component via HACS and wanted to again add my workaround and was very pleased to already found it in the code. Finally I can just hit update without needing to spend extra time patching. Guess the issue can probably be closed now, at least from my side.

cpuks commented 1 year ago

I've got a different yet somewhat similar use case I cannot get to work: A few years ago I've installed HVAC duct system for 5 rooms on the upper floor of my house. As for duct systems it usually gives the same airflow to every room which has to be adjusted via anemostat. Recently I've added stepper motors to all anemostats to be able to open individually in each room (all works on ESPHome) I've got entity cover which is in between 0-100%. I would like to create thermostat now based on temp sensor and anemostat stepper motor position. Is it possible? That's my config:

  - platform: smart_thermostat
    name: biblioteka termostat
    unique_id: biblioteka_termostat
    heater: cover.biblioteka_anemostat
    target_sensor: sensor.biblioteka_higrometr_ble_temperature
    min_temp: 19
    max_temp: 28
    ac_mode: True
    target_temp: 21
    keep_alive:
      seconds: 60
    away_temp: 14
    kp: 5
    ki: 0.01
    kd: 500
    pwm: 0
    cold_tolerance: 0.8
    hot_tolerance: 0.8

Heater has to be either switch with 0 / 1 or what else? Thanks in advance.

Ok so dummy heater switch and control_output in % to start working then fine tune

gryzli133 commented 1 month ago

would it work with heater as a pwm light (esphome)? I have a thermo-electric actuators for the floor heater. They are 24V and possible to control over PWM (not really linear, but still controllable). But I cannot get it work as heater:

climate:
  - platform: smart_thermostat
    name: Termostat Salon
    unique_id: smart_thermostat_salon
    heater: light.parter_pwm_salon
    target_sensor: sensor.atc_2f27_temperature
    min_temp: 7
    max_temp: 28
    ac_mode: False
    target_temp: 19
    keep_alive:
      seconds: 60
    away_temp: 14
    comfort_temp: 22
    sleep_temp: 20
    boost_temp: 25
    boost_pid_off: true
    sensor_stall: 600
    output_safety: 30
    kp: 5
    ki: 0.01
    kd: 500
    pwm: 0

I get this fault:

The number.set_value service called the climate.set_hvac_mode service, which was not found.

I found a workaroud:

But it would be nice to get it work streight out of the Smart Thermostat.

ScratMan commented 3 weeks ago

would it work with heater as a pwm light (esphome)? I have a thermo-electric actuators for the floor heater. They are 24V and possible to control over PWM (not really linear, but still controllable). But I cannot get it work as heater:

I found a workaroud:

  • heat is input_number
  • automation is writing input number value to the PWM as light brightness (0-100%)

But it would be nice to get it work streight out of the Smart Thermostat.

Should work out of the box now with v2024.6.1