Dew point calculator

Continuing the discussion from Anyone have the Aeotec AerQ?:

@JohnRob do you have code for a driver to go along with your dew point app? Would you mind sharing? Thanks!

Calculating dewpoint is surprisingly complicated. There are simple approximations, but usually only valid for a small range of temperatures and/or humidity ranges. If you are looking for outdoor dewpoint it would be much easier to rely on forecast data from a third party.

I'm curious what your application is. If it is a simple one, you may be able to tailor-fit an equation for your expected conditions.

See this thread for more:

:exploding_head:
I’ve been using your driver for a while now and never noticed those options to enable dew point, heat index and summer simmer index calculations.

Thanks!

2 Likes

Yes.

I use the August-Roche-Magnus approximation, which is fairly accurate over a wide temperature and relative humidity range.

3 Likes

It sounds like you are already good to go but I'll post my code FYI.
John

    /*
This code loads and subscribes to the selected device capabilities.
2020-07-01 JohnRob

Need to:
	Consider averaging multiple sensors, writing a DEW point Virtual Driver
*/

definition(
    name: "DEW Point Calculator",
    namespace: "hubitat",
    author: "JohnRob",
    description: "DEW Point Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
	page(name: "mainPage")
}

def mainPage() {
	dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
		section {
			input "thisName", "text", title: "Name this DEW Point Calculator", submitOnChange: true
			if(thisName) app.updateLabel("$thisName")
			input "tempSensor", "capability.temperatureMeasurement", title: "Select Temperature Sensor", submitOnChange: true, required: true, multiple: false
			input "humidSensor", "capability.relativeHumidityMeasurement", title: "Select Humidity Sensor", submitOnChange: true, required: true, multiple: false
		} // section
	}	// dymanicPage
}	// mainPage

def installed() {
	initialize()
}

def updated() {
	unsubscribe()
	initialize()
}

def initialize() {

  	def averageDev = getChildDevice("DEWPoint_${app.id}")
	if(!averageDev) averageDev = addChildDevice("hubitat", "Virtual Temperature Sensor", "DEWPoint_${app.id}", null, [label: thisName, name: thisName])
	averageDev.setTemperature(0)
	subscribe(tempSensor, "temperature", handlerTEMP)
	subscribe(humidSensor, "humidity", handlerHUMID)

	state.lastHUMID = 50		// these are in the app and will not display in the child
	state.lastTEMP = 50		//  50/50 DEWPoint = 32
}

def calcDEW() {
	def averageDev = getChildDevice("DEWPoint_${app.id}")
    log.debug "  56 state.lastTEMP ${state.lastTEMP}"
    log.debug "  57 state.lastHUMID ${state.lastHUMID}"
    operandHUMID = state.lastHUMID.toDouble()
    operandTEMP = state.lastTEMP.toDouble()
    
    def dewPoint = (operandTEMP - (9 / 25) * (100 - operandHUMID))
    log.debug "   62  dewPointI =     ${dewPointI}"
	averageDev.setTemperature(dewPoint.toInteger())
	//return
}

def handlerHUMID(evt) {
	state.lastHUMID = evt.value
    log.debug " 65 last Humidity = ${evt.value}"
   calcDEW()
}

def handlerTEMP(evt) {
	state.lastTEMP = evt.value
    log.debug " 71 last Temperature = ${evt.value}"
   calcDEW()
}

//  --- eof ---

A short background:

**My first app and how I stumbled through it.**

The goal of this post is to pass on what I learned writing my first App.  Its a simple app but it's an example of many of the basics.

This app calculates the DEW point from a temperature reading and a humidity reading.  It is somewhat rough but functions correctly.  I believe     it is best to present it here at this point so I can explain what I learned writing this app.

Before I go on I would like to thank @bravenel , @bertabcd1235, and @csteel who helped me directly. In addition there are numerous posters who     have helped by writing informative post where I could glean information to help me understand.

OK,

Description of the needed steps my approach: (could be presented as a flow chart)
* 	Select a single temperature from among my temperature sensors
* 	Select a single humidity from among my humidity sensors
* 	Calculate the DEW point (in °F) whenever either the selected temperature of selected humidity changes.  To accomplish this the App had to     "remember" both the temperature and humidity so when one of them changes the "last" other can be used in the calculation.
* 	I found the best place to store these values were as state variables:
* 	They became state.lastTEMP and state.lastHUMID.  Capitalization matters!  Also note the state variables are all stored as strings, even     though they can store strings, boolean, integer etc.  When the state variables are read they are of type "string". When the App stores  57.3 in     a ""state.lastTEMP".  When read it back it will be the string "57.3".  We'll find later on this can cause some complications when we try to use     them in a calculation.

So how to display my result..... It seems the only way is to create a ChildDevice, I was hoping not to get this complicated but there is no     other way and it turns out ChildDevices are not that difficult.  The childDevice is created by this app as long as the childDevice can be one     of the already defined Virtual devices Hubitat has built in.   There was no DEW Point Virtual device so I used the virtual temperature device.      I learned this by following the below mentioned example from Hubit.

My first hurdle is understanding program flow and why things happen as they do.  I could find little in the way of a meaty overview, so I'll     try here.  Caution there will be some misstatements which I hope others can correct but this represents my understanding.

I started with the AverageTemperature.groovy example on the hubitat github site.

I learned the order of the code blocks (aka methods, in other languages subroutines etc) don't have any specific requirements.  With the     exception of the "definition" and "preferences" the rest can be in any order you chose.

**The Program:**

*"definition"*

This simply provides information to the Hub so the UI can show the name of the app, author etc.   I know it can be expanded upon but I've not     found a definitive syntax reference.  It can be formatted as I have or placed all on one line.

*"preferences"*

I'm really not sure what preferences does in the example I followed.  In seems to refer to the "mainPage".  I suspect in an app where there are     multiple pages it likely has a more important roll.

The rest of the code is a collection of methods (subroutines in other languages).   The order of execution is:

* installed()  - runs when driver/app is installed, via pair or virtual
* configure()  - runs when driver/app is installed, after installed is run. if capability Configuration exists, a Configure command is added to     the ui
* initialize() - runs first time driver/app loads, ie system startup when capability Initialize exists, a Initialize command is added to the ui.
* updated()    - runs when save is clicked in the preferences section

The execution of the remaining methods is dictated by the code, starting with either installed() or updated() and always ends calling     initialize(). 

The below explanation references code line numbers.  If you copy the App code (below) into the Hubitat UI you will be able to see the     referenced line numbers.

So we will start with initialize():
because we are using a ChildDevice to display the the DEW Point results we first must find if it already exists or must we create one.

WHERE/WHEN DO INPUTS GET CREATED?????????

Line 44 we ask for a copy of the object "DEWPoint_$${app.id}" and assign it to averageDev.  The value of app.id is somehow the magic of the Hub     OS, we don't ever need to consider its value.  For the curious, the app.id with be in the device page DNI column of the created ChildDevice.

Line 45 tests if a valid object was returned.  If not there is no childDevice and one must be created.
Because we chose to use the "Virtual Temperature Sensor" device, it has a capability "temperature".  This value is set by:     "averageDev.setTemperature"(some value).
Line 46 initialized the DEW Point Temperature to 0

Now we must watch the two selected
2 Likes

This looks to be the part where the magic happens. As I made some bad assumptions on a similar equation earlier this evening, can you elaborate on whether the temperature is in degrees C, F, K, etc. And any other bits of wisdom someone like me may need to do something similar using Rule Machine?

Just to confirm, if I turn on a few options for a device, then subsequently turn one off.

The attribute for the value that’s no longer updating will stay populated with whatever the last calculated value was, is that correct?

Obviously I'm not @JohnRob, but it looks like it has to be in Fahrenheit.

Its in °F I noted it in the background file, I should put it in the program code as a comment.

Just so you know. This is not a full featured app it is just enough to get a Dew point number to know when our outside critters should be inside.

2 Likes

OK. Easey Peasy.

Just use an inexpensive Sonoff temperature / humidity sensor and the Guffman's virtual dew point app.

1 Like

JohnRob - how do I go about getting the calculated dewpoint number if I want to use it in an app or display it on my dashboard?

Hi, Welcome to the community.

Install the below App and Driver. The App will allow you to select the temp and humidity input.
The driver will present you with a variable you can put in you dashboard.

Application:

    /*
Dew Point App V003

2020-07-05
    Think we don't need to store the lastTEMP and lastHUMID.  We can just read them when we need     to make a calc.
2020-07-20 (open) added Virtual DewPoint Calc device to display the below results.

*/

definition(
    name: "DEW Point Calculator",
    namespace: "hubitat",
    author: "JohnRob",
    description: "DEW Point Calculator",
    category: "Convenience",
    iconUrl: "",
    iconX2Url: "")

preferences {
    page(name: "mainPage")
}

def mainPage() {
    dynamicPage(name: "mainPage", title: " ", install: true, uninstall: true) {
        section {
            //log.debug ("   25   beginning of section")
            input "thisName", "text", title: "Name this DEW Point Calculator", submitOnChange:     true
            if(thisName) app.updateLabel("$thisName")
            input "tempSensor", "capability.temperatureMeasurement", title: "Select Temperature     Sensor", submitOnChange: true, required: true, multiple: false
            input "humidSensor", "capability.relativeHumidityMeasurement", title: "Select     Humidity Sensor", submitOnChange: true, required: true, multiple: false
            //log.debug ("  30   end of section")
        } // section
    }   // dymanicPage
}   // mainPage

def installed() {
    initialize()
}

def updated() {
    unsubscribe()
    initialize()
}

def initialize() {
    //log.debug ("  45   begin initialize")

    def dewpointDev = getChildDevice("DEWPoint_${app.id}")
    if(!dewpointDev) dewpointDev = addChildDevice("hubitat", "Virtual DewPoint",     "DEWPoint_${app.id}", null, [label: thisName, name: thisName])
    dewpointDev.setDewPoint(0)
    subscribe(tempSensor, "temperature", handlerTEMP)
    subscribe(humidSensor, "humidity", handlerHUMID)

    state.lastHUMID = 50        // these are in the app and will not display in the child
    state.lastTEMP = 50     //  50/50 DEWPoint = 32
}

def calcDEW() {
    def dewpointDev = getChildDevice("DEWPoint_${app.id}")
    //log.debug "  56 state.lastTEMP ${state.lastTEMP}"
    //log.debug "  57 state.lastHUMID ${state.lastHUMID}"
    operandHUMID = state.lastHUMID.toDouble()
    operandTEMP = state.lastTEMP.toDouble()
    
    def dewPoint = (operandTEMP - (9 / 25) * (100 - operandHUMID))
    //log.debug "   62  dewPoint =     ${dewPoint}"
    dewpointDev.setDewPoint(dewPoint.toInteger())
    //return
}

def handlerHUMID(evt) {
    state.lastHUMID = evt.value
    //log.debug " 65 last Humidity = ${evt.value}"
   calcDEW()
}

def handlerTEMP(evt) {
    state.lastTEMP = evt.value
    //log.debug " 71 last Temperature = ${evt.value}"
   calcDEW()
}

//  --- eof ---


//tempSensor.currentTemperature
//humidSensor.currentHumidity

Driver:

    // V0.01  Virtual Dew Point Device

metadata {
    definition (name: "Virtual DewPoint", namespace: "johnrob", author: "several") {
        capability  "Sensor"
        command     "setdewPoint", ["NUMBER"]   // this will be a method.  [] may cause an input     box to be created must test.
        attribute   "dewPoint", "Number"      // this will go into the Hub database

    }
    preferences {       // These become entries in the "device" page to ask us for our     preferences!
        input name: "txtEnable", type: "bool", title: "Enable descriptionText logging",     defaultValue: true
    }
}

def installed() {
    log.warn "installed..."
    setdewPoint(0)
}

def updated() {
    log.info "updated..."
    log.warn "description logging is: ${txtEnable == true}"
}

def parse(String description) {
}       // basically useless.  Included because it is included in the template.

def setDewPoint(dewpoint) {
    //log.debug "   29  dewpoint =   ${dewpoint}"

    def descriptionText = "${device.displayName} was set to $dewpoint"
    if (txtEnable) log.info "${descriptionText}"
    sendEvent(name: "dewPoint", value: dewpoint, unit: "°F", descriptionText: descriptionText)
}

/*
The setOpenVentArea is a command for that driver. An app can call it with

myVent.setOpenVentArea(myArea)

Doing so causes the event to be sent, and that in turn sets the attribute called openVentArea.
*/
1 Like

Thanks! I missed where you posted the driver. I appreciate your explanations about how you wrote that app.

Hey,

Thanks for posting this! Thread hasn't been touched in a while but I just tried this out.... No groovy expert and it seems to have worked for others.... But Log file looked like I was getting an error because the app was installing in namespace johnrob as was the driver, but then the app was looking for the device type in the hubitat namespace instead? I modified it to look in the johnrob namespace to initials the driver and it looks like it works now? (Still reading zero, but it wasn't even initializing before so I think it just needs one of the input values to change?)

Also, FYI for others... That equation is apparently accurate above approx ~50% RH... Quick spot check on a psychometric chart seems to confirm, gets a bit off below 50%.

Yea I wrote this before I had a good grasp on "namespace" now I make everything "hubitat" unless there is a risk of another program with the same name.