This is a reasonable job for OH.
I’ll ignore the issue of how to get OH to talk to your fan (I use MQTT to talk to a RasPi wired through GPIO to a relay to open my garage. Similarly I have Reed switches wired to RasPi GPIO which report their open/closed to OH over MQTT). There is plenty on this forum and elsewhere to help with that. And if you run into trouble ask specific questions and I and others will be happy to help.
Assuming you have everything hooked up so OH can send and receive info to/from your devices, the rest would be implemented in Rules. Rules are Turing complete so if you can think of it and it can be computed, one can write a rule to do it. I actually do something pretty similar with my HVAC. I have a forced air heater but no air conditioner. Most of the time that isn’t a problem but it can get hot upstairs during the summer and the basement is usually 10-15 degrees F cooler than the top floor. So I turn on the fan when the inside temp is greater than the target temp and the outside temp (based on the weather forecast) is greater than the target.
I use a Nest but the basic logic should apply to your situation. At a minimum it should give you a starting point.
NOTE: I’ve posted my full Nest rules and Items without simplifying them. Ask questions if you see something you don’t understand.
#Items
// Persistence Groups
Group gMyOpenhab // for Items that are exposed to IFTTT
Group gChart // for Items to persist for charting
Group gHistory // for Items to preserve their history
//Group gRestore // for Items to restore on startup (currently everything so commented out)
Number N_V_NestHumidity "Main Floor Humidity [%d %%]" <temperature> (gChart) { nest="<[thermostats(Entryway).humidity]" }
Number N_V_NestCurrTemp "Main Floor Ambient Temp [%d °F]" <temperature> (gChart) { nest="<[thermostats(Entryway).ambient_temperature_f]" }
Number Nest_Temp_Chart_Period "Chart Period"
Number Nest_Hum_Chart_Period "Chart Period"
Number S_V_NestTargetTemp "Furnace Target Temp [%.1f °F]" <temperature> { nest="=[thermostats(Entryway).target_temperature_f]" }
String S_C_NestAway "Nest Away Status [%s]" <present> { nest="=[structures(Wuthering Heights).away]" }
Switch S_C_NestFan "Fan Active [%s]" <socket> { nest="=[thermostats(Entryway).fan_timer_active]" }
Switch N_NestOnline "Nest Status [%s]" <network> { nest="<[thermostats(Entryway).is_online]" }
Number Weather_Temperature "Outside Temperature [%.0f] °F" <temperature> (gChart) { weather="locationId=home, type=temperature, property=current, unit=fahrenheit" }
#Rules
import org.openhab.core.library.types.*
import org.joda.time.*
var DateTime openWindowsNotif = null
var DateTime closeBlindsNotif = null
rule "Set Nest to Away if No One is Home"
when
Item Present changed
then
if(Present.state != OFF) {
logInfo("Nest", "Setting Nest to HOME")
S_C_NestAway.postUpdate("home")
}
else {
logInfo("Nest", "Setting Nest to AWAY")
S_C_NestAway.postUpdate("away")
}
end
rule "Turn on fan when it gets hot"
when
Time cron "0 0/30 * * * ?" or // in case the temps don't change but it is still too hot
Item N_V_NestCurrTemp changed or
Item S_V_NestTargetTemp changed
then
// Get current temps
Thread::sleep(5000) // don't overload the Nest server by asking for too much too fast
val curr = N_V_NestCurrTemp.state as DecimalType
Thread::sleep(5000)
val tgt = S_V_NestTargetTemp.state as DecimalType
val outside = Weather_Temperature.state as DecimalType
Thread::sleep(5000)
// If outside is hotter than our target, this is a good check to disable the rule in the winter
if(tgt < outside) {
// Inside is warmer than target, fan isn't already on
if(curr > (tgt + 1) && S_C_NestFan.state != ON){
logInfo("Nest", "The temp is " + curr + ". Turning on the fan")
S_C_NestFan.sendCommand(ON)
}
// Inside is cooler than target and fan is on
else if(curr <= tgt && S_C_NestFan.state == ON) {
logInfo("Nest", "The temp is " + curr + ". Turning off the fan")
S_C_NestFan.sendCommand(OFF)
}
// Send recommendation alerts during the day when someone is home
if(TimeOfDay.state.toString == "Day" && Present.state == ON){
// Inside is warmer than outside, open the windows
if(curr > outside && (openWindowsNotif == null || openWindowsNotif.isBefore(now.minusHours(24)))) {
Notification_Proxy_Alarm.postUpdate("It is cooler outside ("+outside+") than inside ("+curr+"), consider opening the windows")
openWindowsNotif = now
}
// Inside it cooler than outside, close the blinds
else if(curr < outside && (closeBlindsNotif == null || closeBlindsNotif.isBefore(now.minusHours(24)))){
Notification_Proxy_Alarm.postUpdate("It is warmer outside ("+outside+") than inside ("+curr+"), consider closing the blinds")
closeBlindsNotif = now
}
}
}
end
#Sitemap
// Climate Control
Text item=N_V_NestCurrTemp {
Switch item=S_C_NestAway mappings=[home="Home",away="Away"]
Switch item=S_C_NestFan
Text item=N_V_NestCurrTemp {
Frame{
Switch item=Nest_Temp_Chart_Period label="Period" mappings=[0="Hour", 1="Day", 2="Week"]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=h" refresh=6000 visibility=[Nest_Temp_Chart_Period==0, Nest_Temp_Chart_Period=="Uninitialized"]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=D" refresh=30000 visibility=[Nest_Temp_Chart_Period==1]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestCurrTemp,Weather_Temperature&period=W" refresh=30000 visibility=[Nest_Temp_Chart_Period==2]
}
}
Text item=N_V_NestHumidity{
Frame{
Switch item=Nest_Hum_Chart_Period label="Period" mappings=[0="Hour", 1="Day", 2="Week"]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=h" refresh=6000 visibility=[Nest_Hum_Chart_Period==0, Nest_Hum_Chart_Period=="Uninitialized"]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=D" refresh=30000 visibility=[Nest_Hum_Chart_Period==1]
Image url="http://user:password@localhost:8080/rrdchart.png?items=N_V_NestHumidity,Weather_Humidity&period=W" refresh=30000 visibility=[Nest_Hum_Chart_Period==2]
}
}
Setpoint item=S_V_NestTargetTemp minValue=50 maxValue=80 step=1
Text item=N_NestOnline
}
}
#Persistence
// persistence strategies have a name and a definition and are referred to in the "Items" section
Strategies {
// for rrd charts, we need a cron strategy
everyMinute : "0 * * * * ?"
default = everyChange
}
Items {
// additionally persist weather info every minute
gHistory*,gChart* : strategy = everyUpdate, everyMinute
}
Theory of Operation:
Every 30 minutes , when the current inside temp changes, and when the target temp changes trigger a rule to see if the fan needs to be turned on.
In the rule, if our target temp is less than the current outside temp (obtained from the Weather binding) see if we should turn on the fan.
If the current temp is warmer than the target + 1 degree and the fan isn’t already on, turn on the fan (NOTE: nest will only keep the fan on for 30 minutes then shut it off automatically).
If the current temp is cooler than the target and the fan is running, turn the fan off.
NOTE: Our house does not change temperature that fast and turning on the fan does not really drop the inside temp but instead keeps it from raising or raising as fast as it otherwise would so for my setup I don’t need to worry too much about rapidly cycling the fan when we current inside temp and target temp are really close together.
That is the main part of the logic you care about. The rule then goes on to send a notification (no more than once per day) to open the windows or close the blinds when it is appropriate.
The sitemap looks like this:
You can configure your three-way setting the same way I handle the chart period on the sitemap above.
In order for charting to work I configured persistence. I use rrd4j which requires values be saves at least once per minute for charting to work. So you will notice that each of the Items that I create charts for are members of the gChart group which causes them to be persisted so I can create charts.
NOTE: What you see with these charts is what you get. If you want something configurable or more attractive you must save to an externally accessible database (e.g. MySQL) and use a third party charting tool.
You will need to do a good deal of customization to this but it should be enough to get you started.