Calculating the Relative Humidity (not as simple as it sounds)

You can do Math in templates if you go to the Templates tab you can test it out

For example {{100*(50/100)}} will out put 50.0

Then in your automation

-id: 0000
  alias: exchanger  Start
  trigger:
  - at: '10:30:00'
    platform: time
  condition:
  - condition: template
    value_template: '{{ (10+8) < (80+5) }}'
  action:
    service: exchanger.start

where (10+8) represents you inside humidity and (80+5) represents your outside humidity

Hello!

Have you tried the Node-Red add-on? You can use a simple function that reads your variables upon change or at a certain time and based on that you can opt to trigger a humidifier on/off.

I personally am using Aquara environment monitors that return temperature and humidity inside my kids’ room and based on the value decide to turn on/off their humidifier. You can easily tweak this flow to suit your needs if you’re up to it. Let me know and I’m happy to post it and connect with you to help.

Cheers,

W.

Node Red was really unreliable for me. I might revisit it.

I’m going to check out templates, is there a limit to the functions that I can use in there?

Create a template sensor.

value_template: "{{ 100*(e**((17.625 *TD)/(243.04+TD))/e**((17.625* T)/(243.04+T))) }}"

Replace TD and T with your sensors.

e.g. for TD, it could be something like this states('sensor.outside_dew_pt') | float

That’s amazing, thank you so much for taking the time to do that for me!

So I’m getting an error, but I don’t really understand what the issue is:

[homeassistant.components.sensor.template] Template sensor humidity_true_inside has no entity ids configured to track nor were we able to extract the entities to track from the value template(s). This entity will only be able to be updated manually.

The sensor values are:

sensor.bedroom_temperature
20.0
unit_of_measurement: °C friendly_name: Bedroom Temperature

And:

Sensor.pws_dewpoint_c
-4
attribution: Data provided by the WUnderground weather service date: Last Updated on February 7, 7:40 PM EST unit_of_measurement: °C friendly_name: Dewpoint icon: mdi:water

You need to explicitly tell the sensor template which entities to monitor.

Post the code you have for the template sensor and I’ll be able to help.

"{{ 100*(e**((17.625 *('sensor.pws_dewpoint_c')|float)/(243.04+('sensor.pws_dewpoint_c')|float))/e**((17.625*('sensor.bedroom_temperature')|float)/(243.04+('sensor.bedroom_temperature')|float))) }}"

  • platform: template
    sensors:
    humidity_true_inside:
    value_template: “{{ 100*(e**((17.625 (‘sensor.pws_dewpoint_c’)|float)/(243.04+(‘sensor.pws_dewpoint_c’)|float))/e*((17.625*(‘sensor.bedroom_temperature’)|float)/(243.04+(‘sensor.bedroom_temperature’)|float))) }}”

Thank you!

1 Like

Try this:

- platform: template
  sensors:
    humidity_true_inside:
    friendly_name: True Inside Humidity
    entity_id:
      - sensor.pws_dewpoint_c
      - sensor.bedroom_temperature
    value_template: "{{ 100 * (e**((17.625 * states('sensor.pws_dewpoint_c')|float)/(243.04 + states(‘sensor.pws_dewpoint_c’)|float))/e**((17.625 * states('sensor.bedroom_temperature')|float)/(243.04+ states('sensor.bedroom_temperature')|float))) }}"

The defined entity ids tell HA what to monitor to update the template.
You also missed the “states” part of states(‘sensor.pws_dewpoint_c’) in your template. Same for the bedroom temp sensor (fixed above).

So this is now giving a value, but it is constantly 100.

I need to check our the math

I once started doing this for absolute humidity by using the python_script component. Even though you may have your answer, an alternative approach might be interesting for comparison.

Here’s the script (it’s just logging everything):

# Compute the absolute humidity in g/m³
# Param rh - relative humidity in %
# Param t - temperature in °C
def ah(rh, t):
    mw = 18.016 # kg/kmol (Molecularweight of vapor)
    rs = 8314.3 # J/(kmol*K) (Universal Gasconstant)
    svp = 6.112 * math.exp((17.67*t)/(243.5+t)) # Compute saturated water vapor pressure in hPa
    vp = rh/100. * svp # Compute actual water vapor pressure in hPa
    return 10**5 * mw/rs * vp/(t + 273.15)

humidity_entity = data.get('humidity_entity')
humidity_attribute = data.get('humidity_attribute')
temperature_entity = data.get('temperature_entity')
temperature_attribute = data.get('temperature_attribute')

if humidity_entity is not None and temperature_entity is not None:
    if humidity_attribute is not None:
        logger.warning("Humidity attr: {}".format(humidity_attribute))
        humidity = float(hass.states.get(humidity_entity).attributes[humidity_attribute])
    else:
        humidity = float(hass.states.get(humidity_entity).state)
    if temperature_attribute is not None:
        logger.warning("Temperature attr: {}".format(temperature_attribute))
        temperature = float(hass.states.get(temperature_entity).attributes[temperature_attribute])
    else:
        temperature = float(hass.states.get(temperature_entity).state)
    
    logger.warning("Humidity: {}".format(humidity))
    logger.warning("Temperature: {}".format(temperature))
    
    absolute_humidity = ah(humidity, temperature)
    logger.warning("Absolute Humidity: {}".format(absolute_humidity))
    #hass.bus.fire(name, { "wow": "from a Python script!" })
else:
    logger.error("Missing entities in service data.")

To use this you would you would call the service with data like this:

{
    "humidity_entity":"sensor.myhumiditysensor",
    "temperature_entity":"climate.mythermostat",
    "temperature_attribute":"current_temperature"
}

So it’s using existing entities, just like the template approach. In my example I used a climate entity for the temperature where the current temperature is exposed via an attribute. But it could also be a sensor like I do it for the humidity.

3 Likes

That’s an interesting approach. Did this actually work? I’m just getting a value of 100, which when I do the math means that there are no values for either sensor, but this doesn’t make a whole load of sense as the sensors actually have values.

Where do you put the Python script? And where do you call the service? In HA?

Use the developer tools template editor to play with your equation.

What was your source of the equation?

If you can post that I can check the formula for order of operations problems.

Tom, I put the calculation in a spreadsheet and it returns the correct values! Therefore I think there is an issue with my sensor values…

Excel and HA may have a different order of operations.

What happens when you put your sensors in the template editor?

I’m still getting 100. I’ll post more tomorrow

It does generate a value. I am however not too involved in that topic to judge if the result makes any sense. It’s what I implemented based on my Google research.

Follow the python_script instructions. Once you have placed the script at the correct location etc., you shoul see the script as a service in the service overview panel of HA. There you can feed it with JSON just like I have posted above. The result will be logged. Just below the line that does the final log there’s a commented out line I took from a template that would generate an event. But you probably could also modify an entity. I don’t know about that. So that’s the part you have to figure out, as it depends on you how you want this to work.

I tried to prettify the Jinja2 code + using even more accurate constants.
(huge thanks for @danielperna84, for rhTdFromWetBulb.pdf and for this book)

Here is my solution:


- platform: template
  sensors:
    o1balcony_ah:
      friendly_name: 'Outside: Abs. humidity'
      entity_id:
        - sensor.o1balcony_xiamul_t
        - sensor.o1balcony_xiamul_h
      device_class: humidity
      unit_of_measurement: 'g/m³'
      availability_template: >
        {{ not is_state('sensor.o1balcony_xiamul_t', 'unavailable') and not is_state('sensor.o1balcony_xiamul_h', 'unavailable') }}
      value_template: >
        {%- set t = states('sensor.o1balcony_xiamul_t') | float %}
        {%- set rh = states('sensor.o1balcony_xiamul_h') | float %}
        {%- set mw = 18.016 %}                                       {#- kg/kmol (Molecularweight of vapor) #}
        {%- set rs = 8314.3 %}                                       {#- J/(kmol*K) (Universal Gasconstant) #}
        {%- set svp = 6.112 * (e ** ((17.67 * t) / (243.5 + t))) %}  {#- Compute saturated water vapor pressure in hPa #}
        {%- set vp = rh / 100.0 * svp %}                             {#- Compute actual water vapor pressure in hPa #}
        {{ (10 ** 5 * mw / rs * vp / (t + 273.15)) | round(1) }}
2 Likes

And a follow-up in the new template form:

- sensor:                                                                                                               
    - name: o1balcony_ah                                                                                                   
      unique_id: d698a9fe-6c31-4351-a821-704ba488cddb                                                                   
      device_class: humidity                                                                                            
      icon: 'mdi:water'                                                                                                 
      unit_of_measurement: 'g/m³'                                                                                       
      availability: >                                                                                                   
        {{ not is_state('sensor.o1balcony_xiamul_t', 'unavailable') and not is_state('sensor.o1balcony_xiamul_h', 'unavailable') }}                 
      state: >                                                                                                          
        {%- set t = states('sensor.o1balcony_xiamul_t') | float %}                                                                    
        {%- set rh = states('sensor.o1balcony_xiamul_h') | float %}                                                                   
        {%- set mw = 18.016 %}                                       {#- kg/kmol (Molecularweight of vapor) #}          
        {%- set rs = 8314.3 %}                                       {#- J/(kmol*K) (Universal Gasconstant) #}          
        {%- set svp = 6.112 * (e ** ((17.67 * t) / (243.5 + t))) %}  {#- Compute saturated water vapor pressure in hPa #}
        {%- set vp = rh / 100.0 * svp %}                             {#- Compute actual water vapor pressure in hPa #}  
        {{ (10 ** 5 * mw / rs * vp / (t + 273.15)) | round(1) }}
3 Likes

Thanks for this, everyone!

I appreciate this is a very old thread, but here is the same calculation as a macro (which I keep in /custom_temlates/humidity.jinja):

{% macro ah(temp,rel_humid) %}
  {% set t    = temp |float  %}                 {# Temp [°C] #}
  {% set rh   = rel_humid |float %}             {# Rel. humidity [%] #}
  {% set tk   = t + 273.15 %}                   {# Temp [°K] #}
  {% set mw   = 18.016 %}                       {# Molecularweight of vapor [kg/kmol] #}
  {% set rs   = 8314.3 %}                       {# Universal Gas constant [J/(kmol*K)] #}
  {% set svp  = 6.112*e**(17.67*t/(t+243.5)) %} {# Saturated water vapor pressure [hPa] #}
  {% set vp   = rh / 100.0 * svp %}             {# Actual water vapor pressure [hPa] #}
  {{ (10**5 *mw/rs *vp/tk) |round(1) }}         {# Abs. humidity [g/m³] #}
{% endmacro -%}

I added tk to make the final calculation easier to read.

Below is the reverse calculation (RH from AH). It’s a different method so, if you recalculate the RH from AH, there is a small (~0.5%) error to the RH you started with.

{% macro rh(temp,abs_humid) %}
  {% set t    = temp |float %}                  {# Temp [°C] #}
  {% set ah   = abs_humid |float %}             {# Abs. humidity [%] #}
  {% set tk   = t + 273.15 %}                   {# Temp [°K] #}
  {% set svp  = 6.112*e**(17.67*t/(t+243.5)) %} {# Saturated water vapor pressure, [hPa] #}
  {{ (ah*tk / (svp*2.1764))|round(1) }}         {# Rel. humidity, [g/m³] #}
{% endmacro -%}