One suggestion, to improve the sampling rate (and lower the delay slightly), would be to trigger the rule on āItem GroundFloor_MasterBathroom_Humidity changedā. This would save up to a minute (i.e. when the item state changes just after the cron triggered rule fires).
You could also activate the Shower item based on rate of change, rather than a moving average. In my experience, the humidity level rises very quickly and at a high rate (10-20% per minute), so that would lead to quicker detection than a 5 minute moving average, while still being pretty resistant to false positives.
Good Idea.
In my case, the water pipe divides in the wall.
A clear water pipe I will only get in the shower.
My personal goal was to detect events based on temperature or humidity.
I agree with Bartus, triggering the rule by changes to the humidity rather than periodically would improve its detection speed. And measuring the rate of change would probably work faster too, though that will require more calculations.
From a coding style perspective:
avoid primitives where possible; they cause loading delays on RPis and other SBCs
since nThreshold is a constant, define it as a val, maybe as a global
val nThreshold = 68
rule "TakeAShower"
when
Item GroundFloor_MasterBathroom_Humidity changed
then
var newState = if(GroundFloor_MasterBathroom_Humidity.averageSince(now.minusMinutes(5)) > nThreshold) ON else OFF
if(Shower.state != newState) {
logInfo("TakeAShower", "The shower is now " + newState)
Shower.sendCommand(newState)
}
end
OK, here is a way to calculate the slope. Letās use the slope over one minute. Of course this assumes your sensor reports at least once per minute.
You will probably want to experiment with the time window.
// X-axis is the change in humidity
// Y-axis is the change in time, I'll assume the change in time is always 1
val x1 = GroundFloor_MasterBathroom_Humidity.historicState(now.minusMinutes(1)).state as Number
val x2 = GroundFloor_MasterBathroom_Humidity.state as Number
val slope = 1 / (x2 - x1)
val newState = if(slope > .1) ON else OFF // 10% rise
You will need to experiment with a good slope threshold as well. A negative slope will indicate the humidity is falling. A positive slope indicates it is raising. The bigger the number, the bigger the value.
Iām assuming y1 = 1 and y2 = 2 which will always return 1 in the numerator.
I donāt see any benefit to converting the time to a true Y axis value since we are measuring over the same amount of time every time. So I just hard coded the change in Y to 1.
I should have added a comment that I was making a shortcut there though.
DOH! and I miss labeled my variables. They should be x1 and x2.
I know what you mean, but currently, the 1 / change formula will not result in what we want.
Given x2 is is the current humidity, and x1 is previous, if weāre looking for a positive change (rising humidity value), x2 will be larger than x1. If you divide 1 by the difference, a larger delta in the denominator will result in lower slope (the reverse of what we want).
It will result in a lower absolute slope, but the slope will still move linearly with time. Instead of a reasonable range of 0-100 we would have a reasonable range of 0.0-1.0. Itās still usable.
It probably does make more sense to swap the axises though. If we were to draw it out, the X axis would be assigned the time normally. Then the numbers will actually be closer to the same units of the y axis (i.e. percents).
Second verse, almost the same as the first:
val y1 = GroundFloor_MasterBathroom_Humidity.historicState(now.minusMinutes(1)).state as Number
val y2 = GroundFloor_MasterBathroom_Humidity.state as Number
val slope = y2 - y1
val newState = if(slope > 10) ON else OFF // 10% rise
Yep, apparently Iām all about making things more complicated than they need to be today. Itās just a simple subtraction.
I used to be so good at math. 20 years out of school and now Iāve retained just enough trig to do wood working. Sigh⦠Goodbye calculus and differential equations.
Thank you all, for the constructive ideas.
I will test both variants in parallel and compare the response times and reliability.
And thanks for the push in the direction of Design Pattern: How to Structure a Rule
It will help me to optimize my system and reminds me to spend more time to write more clean and efficient code.
In my experience OpenHAB or any automated system the is intended to promote behavior in Teenagers wonāt work because of their motivated, creative attempts to NOT do whatever you want them to doā¦(speaking only for my kids of course). Iāve found that a small $1.50 windup alarm clock set to 10 minutes, and yelling at them through the door when it goes off works bestā¦J
On a more topical note I have done what @Felix_Raetz was looking to do using an IR sensor in the bathroom and information taken from hot and cold water pulse sensors on the mains water and hot water. Tried using humidity as a trigger for several months but it is just too laggy no matter how you monitor it.
I was just using it to switch on the extractor fans and lights. Found that when someone is in the shower the hot water usage goes up and the IR sensor stops cycling/Triggering with the shower door closed.(Seems it doesnāt see through glass!) I use the high water usage to continue resetting a timer and hold the lights on. Not perfect but in our house we only have constant hot water usage just after the PIR sensor turns off, while showering so it works pretty well.
Nice to see exactly how much water the kids are using tooā¦determines how loud I have to yell at them when that alarm goes offā¦
Been away for a long while and just noticed this topic and thought it was a cool idea and not one Iāve heard of before.
@Felix_Raetz instead of sensing if someone is having a shower why not have a waterproof switch or set of switches that remotely activate the behaviour that you want. Since the person is in the shower anyway then itās as inconvenient as reaching for the shower gel. With a set of switches you could get granularity eg. donāt warm the towel because itās summer; no need to warn the front door callers because Iām not alone in the house; donāt turn music on today because I donāt feel like it. Taken one step further just use Alexa in the shower room.
Guys,
on that note, Iād like to share my solution to this, which I have fine-tuned over the last months and works very reliably. I want to switch on the exhaust fan for a certain amount of time whenever someone takes a shower. Sure, a flood sensor in the shower would work as well, but I want to keep humidity in the bathroom down to prevent mold, so that is the parameter I am using. The issues I was facing were:
Bathroom is pretty big, so the rise in humidity is not as noticeable as it would be in a small bathroom, so I placed the humidity sensor pretty close to the shower itself
Humdity in the house varies with the seasons, higher in summer and lower in the winter
Whenever our cleaning lady mops the floors, humidity throughout the house rises, and I donāt want it to trigger then
So I use other humidity sensors I have throughout the house (I use multisensors for motion detection, and the thermostats have humidity sensors as well) to establish a baseline humidity and calculate on that. So the script basically is triggered by a change in humidity in the bathroom and looks for a rise that is not happening in the rest of the home, and detects that rise by comparing to historical data from 15min minutes ago. I use Influx for that, but rrd would work as well. As it gets triggered on the humidity rise, it also works if the next person showers half an hour later and then cancels and re-sets the timer.
The only situation I have found where it does not work is when the 2nd person showers exactly 15min after the first one started, because then the rise now and 15min ago do not show a delta.
So here it is, ask me if you have any questions. I think most of the variables are self-explanatory. As you can see, the rise in humidity has to be at least 4% and it has to be more than 7% above baseline to trigger. Those are the values that work for me, but YMMV.
var logname = "FanRule"
var Timer t_mbr_fan = null
val hdelta = 4 // 4% Humidity
val humdiff = 7 // 7% Difference to avg. in Home
val int fanTime = 60
rule "Fan Bathroom"
when
Item Sensor_Multi_Master_Bathroom_Humidity received update
then
val pastHum = Sensor_Multi_Master_Bathroom_Humidity.historicState(now.minusMinutes(15),"influxdb").state as Number
val currHum = Sensor_Multi_Master_Bathroom_Humidity.state as Number
val delta = currHum - pastHum
val avgHum = (((Sensor_Multi_Hallway_Office_Humidity.state as Number) + (Sensor_Multi_Hallway_Garage_Humidity.state as Number) + (ecobee_off_actual_humidity.state as Number) + (ecobee_mbr_actual_humidity.state as Number)) / 4 )
val hdiff = currHum - avgHum
// logInfo(logname, "avgHum: " + avgHum "- currHum: " + currHum "- pastHum: " + pastHum; "- delta: " + delta)
logInfo(logname, "avgHum: {}, hdiff:{}, currHum: {}, pastHum: {}, delta: {}", avgHum, hdiff, currHum, pastHum, delta)
if(pastHum < currHum) {
if(delta > hdelta) {
if(hdiff > humdiff) {
if(Switch_Fan_Light_Master_Bathroom_Fan.state != ON) Switch_Fan_Light_Master_Bathroom_Fan.sendCommand(ON) // only send ON if OFF
if (t_mbr_fan !== null) t_mbr_fan.cancel // cancel timer if started
t_mbr_fan = createTimer(now.plusMinutes(fanTime), [ | // start timer
Switch_Fan_Light_Master_Bathroom_Fan.sendCommand(OFF) // when elapsed send OFF
t_mbr_fan = null // reinitialize Timer
])
}
}
}
end
Nothing special, really.
Sensor_Multi_Master_Bathroom_Humidity is a humidity sensor
Switch_Fan_Light_Master_Bathroom_Fan is the fan switch
Both are z-wave, but thatās rather unrelated to this rule.
Please note that getting historic states from influxdb is broken as of now with OH3. But it works with rrd4j.
I have to say that Iāve since moved away from this approach as it didnāt work that reliably when more than one person took a shower within a short timeframe. I now use a door contact for the shower door, which is much easier and a lot simpler.