[SOLVED]How to calculate temperature real feel (feels like, heat index)

Hi all,
Has anyone already done something like this, or has an idea how to do it…
if you read 37C and 63% humidity on your smart home sensor, this would make “feels like” 53C
according to this
https://www.calculator.net/heat-index-calculator.html

is there a way to do this in openhab, anyone knows a magic formula and how to use it in a rule? :slight_smile:

One listed in your link looks promising… Give ir a try in a rule. the forum is to help you develop a solution, not provide a complete one. You need to understand your system to properly maintain it.

The formula below approximates the heat index in degrees Fahrenheit, to within ±1.3 °F (0.7 °C). It is the result of a multivariate fit (temperature equal to or greater than 80 °F (27 °C) and relative humidity equal to or greater than 40%) to a model of the human body.[[1]](https://en.wikipedia.org/wiki/Heat_index#cite_note-SteadmanI-1)[[13]](https://en.wikipedia.org/wiki/Heat_index#cite_note-13) This equation reproduces the above NOAA National Weather Service table (except the values at 90 °F (32 °C) & 45%/70% relative humidity vary unrounded by less than ±1, respectively).

Just try to search in the forum for humidex or heat index. I postst a rule some years ago.

1 Like

See my post: Apparent temperature

thanks, i managed to find it by searching forum for humidex or heat index what was suggested, not sure who’s post I found, maybe yours

this is my rule

rule "set FeelTemperature"
when
 Item BalconyTemperature changed or
 Item BalconyHumidity changed
then
    var Number Temp = BalconyTemperature.state as DecimalType
    var Number Humidity = BalconyHumidity.state as DecimalType
    var Number T = Temp * 9/5 + 32
    var Number RH = Humidity
    var Number HI = 0;

    if(T <= 40.0) {
        HI = T
    } else {
        HI = -42.379 + 2.04901523*T + 10.14333127*RH - 0.22475541*T*RH - 0.00683783*T*T - 0.05481717*RH*RH + 0.00122874*T*T*RH + 0.00085282*T*RH*RH - 0.00000199*T*T*RH*RH

        if (RH < 13 && T >= 80 && T <= 112) {
            var adjust = ((13-RH)/4)  * Math.sqrt(17-Math.abs(T-95)/17)
            HI -= adjust
        } else if (RH > 85 && T >= 80 && T <= 87) {
            var adjust = ((RH-85)/10) * ((87-T)/5)
            HI += adjust
        } else if (T < 80){
            HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (RH*0.094))
        }
    }
    var Number RealFeelTemp = (HI - 32) * 5/9
    FeelTemperature.postUpdate(RealFeelTemp)
end
1 Like

Great you found the post, glad it works, I initially came up with the formula.

1 Like

Does anyone have a formula that also take Wind chill in consideration

From what I can see, the rule itself should work. However, I keep getting a series of conversion errors…

Type mismatch: cannot convert from BigDecimal to Number; line 14, column 568, length 2
Type mismatch: cannot convert from BigDecimal to Number; line 17, column 690, length 2
Type mismatch: cannot convert from BigDecimal to int; line 13, column 545, length 4

… and my attempts to fix it only created more conversion errors. :see_no_evil:

Can anyone confirm that the rule as posted above is working? Or has something within openHAB changed, and the code itself needs an update in the “conversion-area”?

I use lambda, OH 3.3:

val heatIndex = [ Number TT, Number H |	
    val Number T = TT * 9/5 + 32
    var Number HI = 0.0

    if (T <= 40.0) {
        HI = T
    } else {
        HI = -42.379 + 2.04901523*T + 10.14333127*H - 0.22475541*T*H - 0.00683783*T*T - 0.05481717*H*H + 0.00122874*T*T*H + 0.00085282*T*H*H - 0.00000199*T*T*H*H

        if (H < 13 && T >= 80 && T <= 112) {
            HI = HI - ((13-H)/4)  * Math.sqrt(17-Math.abs(T.doubleValue-95)/17)
        } else if (H > 85 && T >= 80 && T <= 87) {
            HI = HI + ((H-85)/10) * ((87-T)/5)
        } else if (T < 80){
            HI = 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (H*0.094))
        }
    }
    Math.round(((HI - 32) * 5/9).floatValue*10.0)/10.0
]

rule "SENS ROOM FeelsLike"
when
    Item roomSens_InWallTemp_R changed
then
        roomSens_FeelsLikeTemp.postUpdate(heatIndex.apply(roomSens_InWallTemp_R.state as Number, roomSens_InWallHum.state as Number)) 


end

Thanks @martiniman, your code runs through without a problem after adjusting to my variable names. Just that the end result (5450 °C) as the final feel temperature seems slightly off. :smiley:

I believe it’s because of the way the temperature and relative humidity is formated on my system.

When I print out those two variables via…

logWarn("notifications", "Temperature: " + Thermostat_Wohnzimmer_ActualTemperature.state.toString + ", Relative humidity: " + Thermostat_Wohnzimmer_Humidity.state.toString)

it reads:

“Temperature: 26.2 °C, Relative humidity: 41 %

Is it possible that the strings “°C” and “%” cause the malfunction? If so, is there a way to get rid of the strings?

Hi! I think this happen because items use “Units Of Measurement”

Thanks.

Do you have an idea on how to remove the unit of measure as an input of the two variables Temperature and Humidity?

I did, as a test, remove the state description, but this did not change anything.

Update: Solved it via the hint mentioned here.

I use the following rule in OpenHab 3.3 to calculate Heat Index (Real Feel):

/**
 * Calculate Heat Index (Real Fell)
 *
 * Based on Adafruit - DHT-sensor-library
 * @see https://github.com/adafruit/DHT-sensor-library/blob/master/DHT.cpp DHT::computeHeatIndex()
 */
rule "Rule 1: Calculate real feel"
when
  Member of gTemp changed or
  Member of gHumid changed
then
  logDebug("climate", 'Rule 1: "Calculate real feel" is executing on triggerItem [{}] changed from [{}] to [{}].', triggeringItemName, previousState, newState)

  if (triggeringItemName.endsWith("_RF")) {
    logDebug("climate", 'Rule 1: "Skipping calculation for a real feel item [{}].', triggeringItemName)
    return;
  }

  val room = triggeringItem.name.split('_').get(0)

  val RealFeelItem = gTemp.members.findFirst[ m |
    m.name == room + "_RF"
  ] as NumberItem;
  if (RealFeelItem === null) {
    logDebug("climate", "Rule 1 @ [{}]: Real feel item [{}] not found.", room, room + "_RF")
    return;
  }
  logDebug("climate", "Rule 1 @ [{}]: Found real feel item [{}].", room, RealFeelItem.name)

  val TempItem = gTemp.members.findFirst[ m |
    m.name == room + "_Temp"
  ] as NumberItem;
  if (TempItem === null) {
    logDebug("climate", "Rule 1 @ [{}]: Temperature item [{}] not found.", room, room + "_Temp")
    return;
  }
  logDebug("climate", "Rule 1 @ [{}]: Found temperature item [{}].", room, TempItem.name)

  val HumidItem = gHumid.members.findFirst[ m |
    m.name == room + "_Humid"
  ] as NumberItem;
  if (HumidItem === null) {
    logDebug("climate", "Rule 1 @ [{}]: Humidity item [{}] not found.", room, room + "_Humid")
    return;
  }
  logDebug("climate", "Rule 1 @ [{}]: Found humidity item [{}].", room, HumidItem.name)

  val Number percentHumidity = (HumidItem.state as QuantityType<Dimensionless>).toUnit("%").toBigDecimal
  logDebug("climate", "Rule 1 @ [{}]: percentHumidity = {}", room, percentHumidity)
  val Number temperature = (TempItem.state as QuantityType<Temperature>).toUnit("°F").toBigDecimal
  logDebug("climate", "Rule 1 @ [{}]: temperature = {}", room, temperature)

  var Number hi = 0.5 * (temperature + 61.0 + ((temperature - 68.0) * 1.2) + (percentHumidity * 0.094));
  logDebug("climate", "Rule 1 @ [{}]: Initial hi = {}", room, hi)

  if (hi > 79) {
    hi = -42.379 + 2.04901523 * temperature + 10.14333127 * percentHumidity +
         -0.22475541 * temperature * percentHumidity +
         -0.00683783 * Math.pow(temperature.doubleValue, 2) +
         -0.05481717 * Math.pow(percentHumidity.doubleValue, 2) +
         0.00122874 * Math.pow(temperature.doubleValue, 2) * percentHumidity +
         0.00085282 * temperature * Math.pow(percentHumidity.doubleValue, 2) +
         -0.00000199 * Math.pow(temperature.doubleValue, 2) * Math.pow(percentHumidity.doubleValue, 2)
    logDebug("climate", "Rule 1 @ [{}]: Correction applied for hi > 79 °F. New hi = {}", room, hi)

    if ((percentHumidity < 13) && (temperature >= 80) && (temperature <= 112)) {
      hi = hi - ((13.0 - percentHumidity) * 0.25) * Math.sqrt((17.0 - Math.abs(temperature.doubleValue - 95.0)) * 0.05882)
      logDebug("climate", "Rule 1 @ [{}]: Correction applied for percentHumidity < 13 %, temperature between 80 °F and 112.0 °F. New hi = {}", room, hi)
    }
    else if ((percentHumidity > 85) && (temperature >= 80) && (temperature <= 87)) {
      hi = hi + ((percentHumidity - 85.0) * 0.1) * ((87.0 - temperature) * 0.2);
      logDebug("climate", "Rule 1 @ [{}]: Correction applied for percentHumidity > 85 % and temperature between 80 °F and 87 °F. New hi = {}", room, hi)
    }
  }

  logDebug("climate", "Rule 1 @ [{}]: Updating real feel item [{}] to {} °F ({}).", room, RealFeelItem.name, hi, new QuantityType<Temperature>(hi + " °F").toUnit("°C"))
  RealFeelItem.postUpdate(hi + " °F")
end

The items are defined like:

Group:Number:AVG gTemp <temperature>
Group:Number:AVG gHumid <humidity>
// ...
Number:Temperature   Room1_Temp           "Room 1 temperature [%.2f °C]"                  <temperature>     (gEsp12f04, gMeasurement, gTemp)               ["Temperature", "Measurement"] { channel="mqtt:topic:tasmota:custom-mcs:2:Temperature"}
Number:Temperature   Room1_RF             "Room 1 real feel [%.2f °C]"                    <man_1>           (gEsp12f04, gMeasurement, gTemp)               ["Temperature", "Measurement"]
Number:Dimensionless Room1_Humid          "Room 1 humidity [%.2f %%]"                     <humidity>        (gEsp12f04, gMeasurement, gHumid)              ["Humidity", "Measurement"]    { channel="mqtt:topic:tasmota:custom-mcs:2:Humidity"}
// ...

Hello All, I stumbled on this looking for the same thing, obviously, heat index, real feel, etc, and compared the code to that the National Weather Service published, here: Heat Index Equation. The code posted does the part called the Rothfusz regression result, which works for many conditions, but… scroll down on the NWS page and we see a situation when the temp is < 80, and one uses a simpler Steadman result before using the Rothfusz approach.

It is entirely possible I have made an error in my interpretation of the NWS method, so if anyone would like a small headache, perhaps a double check is in order? Thanks.

Here is how I translated the NWS description…

float calcRealFeelTemp(float TempC, float RH){
  float T = TempC * 9/5 + 32;   //The formula is for degrees F, easier to convert than modify the formula
  float HI = 0, SteadmanResult, avgTf;    

  if (T <= 40.0) {      // Likely this is where one would do a wind chill calc?   
        HI = T;         // for now, ignore it and return temp.         
                        // https://graphical.weather.gov/definitions/defineApparentT.html
                        // Wind chill temperature = 35.74 + 0.6215T - 35.75V (**0.16) + 0.4275TV(**0.16)
                        // Temp in F, V (wind speed) in statute miles per hour.                                                               
  } else {  
    if (T < 80)   // Temp is between 40 and 80, so, NWS says.... 
    {             // "In practice, the simple formula  HI = 0.5 * {T + 61.0 + [(T-68.0)*1.2] + (RH*0.094)}
                  // is computed first and the result averaged with the temperature. 
                  // If this heat index value is 80 degrees F or higher, the full regression equation 
                  // along with any adjustment as described above is applied." 
      SteadmanResult = SteadmanEqn ( T, RH );    //  Apply the fisrt formula, Steadman, then...
      avgTf = (SteadmanResult + T) / 2;          //  ...take the average          
      if (avgTf < 80){      HI = T;                         
      } else {              HI =  RothfuszEqn( T, RH );     
      }
    } else {
      HI =  RothfuszEqn( T, RH );     
    }   
  }     
  return (HI - 32) * 5/9;  //Convert back to Celsius
}

float RothfuszEqn( float T, float RH )
{
  float adjustment =  0;
  float RothfuszResult =  - 42.379              + 2.04901523*T        + 10.14333127*RH 
                          - 0.22475541*T*RH     - 0.00683783*sq(T)    - 0.05481717*sq(RH)   
                          + 0.00122874*sq(T)*RH + 0.00085282*T*sq(RH) - 0.00000199*sq(T)*sq(RH);
  //  not quite done, checking to see if the adjustment is required...                        
  if (RH < 13 && T > 80 && T < 112) {   // Per NWS, "If the RH is less than 13% and the temperature is
                                        // between 80 and 112 degrees F,  then the following adjustment 
                                        // is subtracted from HI..."  // I took "between" to be non inclusive. 
    adjustment = ((13-RH)/4)  * sqrt(17-abs(T-95)/17);
    RothfuszResult -= adjustment;
  } else if (RH > 85 && T > 80 && T < 87) {   // From NWS, "... if the RH is greater than 85% and the temperature is between 80 and 87 
    adjustment = ((RH-85)/10) * ((87-T)/5);   // degrees F, then the following adjustment is added to HI:    
    RothfuszResult += adjustment;
  }
  return RothfuszResult;
}

float SteadmanEqn (float T, float RH )
{
  return 0.5 * (T + 61.0 + ((T-68.0)*1.2) + (RH*0.094));
}