Hi Folks, go easy on me, this is my first post, thanks in advance to any pointers.
I am trying to achieve the bathroom light to come on and a track to play on my nest hub if there is motion in the bathroom for a minimum of 5 minutes and for this period to reset continually while motion occurs so that it will continue 5 minutes after motion stops.
All appears to be working well, however I am just not happy with how the conditional function operates in the real world. It will behave as expected, however what appears to be happening is that during the period while there is motion in the bathroom the track is triggered to play from the start again, maybe when if the motion trigger goes to idle and then detects motion again I think, but either way it is leaving the loop and trigger the track to start from the start and it just all feels a little clunky. (note i have removed the hyperlink for post, don't worry track URL is ok)
So my question is, do i need a virtual switch or something to say if triggered then don't try and send playtrack to the device again and then reset the virtual device upon the exit of END IF?
Here is my code below using rule machine on a C7, any help would be greatly appreciated.
thanks, Alan.
Trigger ShowerRoomMotionSensor changed
IF (ShowerRoomMotionSensor active) THEN
Cancel Delayed Actions
On: BathroomLightSwitch
Set Volume on Down Stairs Bathroom to 30 --> delayed: 0:00:03
playTrack(hyperlinkremovedrain.mp3) on Down Stairs Bathroom
ELSE-IF (ShowerRoomMotionSensor inactive) THEN
Off: BathroomLightSwitch --> delayed: 0:05:00 (cancelable)
stop() on Down Stairs Bathroom --> delayed: 0:05:00 (cancelable)
END-IF
Welcome to Hubitat!
Yes, you should use something to prevent the music from starting over again. Something that is on/off or true/false. You could use a virtual switch, a global variable, a local variable, or the rule’s private boolean. You would set it to true at the same time you turn on the light and music, and don’t turn it off until you turn off the light and music. (Putting it after the end-if would turn it off every time the rule ran, which isn’t what you want.)
However, in your rule you already have something that is being turned on/off at the correct times- the light bulb. When your sensor goes active, cancel the delayed actions, but then only turn on the light and music if the light is off.
Something like this…
Trigger ShowerRoomMotionSensor *changed*
IF sensor active THEN
cancel delays
IF light is off THEN
turn light on
start music
END IF
ELSE
turn off light delayed 0:05:00 (cancellable)
stop music delayed 0:05:00 (cancellable)
END IF
Hi Gary, thank you so much for your reply and helping hand.
I have chosen a Global variable as I have since introduced an illuminance condition for the light so it will not always be in the on state anymore, but your example with the nested If, End If was invaluable, thanks.
I am conscious that I think like a human and not like a machine when looking at some problems, which can create multiple lines of code that can be achieved in less in many occasions.
The following seems to function perfectly well now, but I would welcome your feedback or criticism on my chosen path if you think I could have tackled it more efficiently, and hopefully the code will help someone else browsing some day.
P.s. forgive my mismatch of Shower and Downstairs Bathroom etc, still working out naming conventions :-s
Trigger ShowerRoomMotionSensor *changed*
IF (ShowerRoomMotionSensor active(F) [FALSE]) THEN
Cancel Delayed Actions
IF (Illuminance of ShowerRoomMotionSensor(205) is < 51(F) [FALSE]) THEN
IF (Variable NestDownStairsBathroomPlayingAudio(false) = false(T) [TRUE]) THEN
On: BathroomLightSwitch
Set Volume on Down Stairs Bathroom to 30 --> delayed: 0:00:03
playTrack(**hyperlinkremoved**rain.mp3) on Down Stairs Bathroom
Set NestDownStairsBathroomPlayingAudio to true
END-IF
ELSE-IF (Illuminance of ShowerRoomMotionSensor(205) is >= 51(T) [TRUE]) THEN
IF (Variable NestDownStairsBathroomPlayingAudio(false) = false(T) [TRUE]) THEN
Set Volume on Down Stairs Bathroom to 30 --> delayed: 0:00:03
playTrack(**hyperlinkremoved**rain.mp3) on Down Stairs Bathroom
Set NestDownStairsBathroomPlayingAudio to true
END-IF
END-IF
ELSE-IF (ShowerRoomMotionSensor inactive(T) [TRUE]) THEN
Off: BathroomLightSwitch --> delayed: 0:05:00 (cancelable)
stop() on Down Stairs Bathroom --> delayed: 0:05:00 (cancelable)
Set NestDownStairsBathroomPlayingAudio to false --> delayed: 0:05:00 (cancelable)
END-IF
Thanks again, Alan.
Alan,
I saw your post earlier and planned on answering later, and I now see you edited it and fixed a couple things I was going to mention. You are now cancelling delayed actions anytime you get an Active trigger, and you added a delay to the set variable command at the very end.
So now the rule should work fine as is, but you are asking about making it more efficient, and there are a couple changes that can be made for that.
If multiple sections of an IF - END IF block have the same actions in them except for one thing, then you can sometimes take just the part that is different and do that conditionally, and all the other stuff can just be listed once…
For both your bright and dim lighting conditions you check the variable, and if it’s false you are setting the volume, starting the playback, and setting the variable to true. The only difference is that you turn on the light only if the illuminance is <51. So it can be shortened to have a conditional statement that turns on the light if it’s <51, and then do the other things unconditionally. So you only need to list them once.
The condition statement for the light can be a one-line “simple conditional” statement, it needs no THEN or END IF, and has no affect on the lines after it. Those other lines will always run if the sensor is Active.
Something like this:
IF the sensor is Active then
Cancel delayed actions
IF playingaudio = false then
IF (illuminance of sensor is <51) On: Bathroomlightswitch <<<<one-line simple conditional statement
Set Volume to 30 delayed: 0:00:03
play the track
Set the PlayingAudio variable to true
END IF
ELSE
turn off the light delayed: 0:05:00 (cancelable)
stop the audio delayed: 0:05:00 (cancelable)
set the variable to false delayed: 0:05:00 (cancelable)
END IF
Also notice that your outer or top level if-endif block only has two possibilities, sensor active or inactive. So if it’s not active you can assume it is inactive (you trigger on both) and just use an ELSE statement without specifying inactive.
Your rule will run fine the way it is, but those are a few things that can be done to simplify it.
1 Like
Thanks so much for taking the time to read and reply!
Absolutely, I didn't even realise the whole inverse check, makes absolute sense to use an Else, you are clearly a Ninja and I am only starting out learning 
The simple condition is a brilliant steer too, I am going to go and scan through the documentation again as my default is to look for an End If, but it is brilliant that this simple function exists and I am sure I have missed some other basic principles.
Thanks so much, that is way more efficient than my version and will certainly help me on my journey. It is so satisfying when it functions as it should.
Thanks again. 