[SOLVED] Night Mode

I try to create a “night mode” which should do following things:

Activated on one switch of the Sonoff T1 3CH:

  1. activate (ON) all other switches in the group “nightModeSwitches”
  2. OFF all in the group “LightsInNightModeOFF”
  3. ON all in the group “LightsInNightModeON”

When deactivating one Night-Mode-Switch OR activating one switch of the group “LightsInNightModeOFF” then:

  1. deactivate (OFF) all other switches in the group “nightModeSwitches”
  2. OFF all in the group “LightsInNightModeON”

Is that even possible? My first attempt with a rule produced an endless loop :frowning:

rule "nightModeSwitches received command ON"
when
    Item nightMode1 received command or
    Item nightMode2 received command or
    Item nightMode3 received command or
    Item nightMode4 received command or
    Item nightMode5 received command or
    Item nightMode6 received command or
    Item nightMode7 received command or
    Item nightMode8 received command
then
    if (receivedCommand == ON) {
        sendCommand(LightsInNightModeOFF, OFF)
        sendCommand(nightModeSwitches, ON)
    }
end

Yes, you created an endless loop by triggering your items on “received command” and then sending commands to all of them from within the rule.

You need a virtual item that determines when night mode is on or off. When that item gets turned on, it triggers a rule with the actions you want to take when night mode is enabled. Here’s a shortened version of mine.

rule "Night Mode"
when
    Item Flag_Night changed to ON
then
    Outlet_Bedside.sendCommand(ON)
    createTimer(now.plusSeconds(5)) 
    [
        Group_Living_Room.sendCommand(OFF)
        Group_Kitchen.sendCommand(OFF)
    ]
end

This rule turns on my bedside light, waits five seconds, and then turns off the lights in my living room and kitchen.

You can then have a separate rule that turns off the night mode virtual item, and runs any other commands you want to fire at the same time. I use the astro binding to turn my night mode off at sunrise.

Good luck!

2 Likes

As the rule will trigger by received command and you will sendCommand to all Triggering Items, there is no surprise about the loop.

Group:Switch nightModeSwitches
Switch nightMode1 (nightModeSwitches)
Switch nightMode2 (nightModeSwitches)
Switch nightMode3 (nightModeSwitches)
Switch nightMode4 (nightModeSwitches)
Switch nightMode5 (nightModeSwitches)
Switch nightMode6 (nightModeSwitches)
Switch nightMode7 (nightModeSwitches)
Switch nightMode8 (nightModeSwitches)

There are two options for a rule:

rule "nightModeSwitches changed"
when
    Item nightModeSwitches changed
then
    if(nightModeSwitches.state == OFF) {
        nightModeSwitches.sendCommand(OFF)
    } else if(nightModeSwitches.state == ON) {
        nightModeSwitches.sendCommand(ON)
        LightsInNightModeOFF.sendCommand(OFF)
        LightsInNightModeON.sendCommand(ON)
    }
end

If this does not work as expected, try this:

var boolean bMyLock = false

rule "nightModeSwitches changed"
when
    Member of nightModeSwitches received command
then
    if(!bMyLock) {
        bMyLock = true
        if(receivedCommand == OFF) {
            nightModeSwitches.members.forEach[i | i.sendCommand(OFF)]
        } else if(receivedCommand == ON) {
            nightModeSwitches.members.forEach[i | i.sendCommand(ON)]
            LightsInNightModeOFF.members.forEach[i | i.sendCommand(OFF)]
            LightsInNightModeON.members.forEach[i | i.sendCommand(ON)]
        }
        createTimer(now.plusSeconds(1),[ |bMyLock = false ])
    }
end

The second rule will lock itself and therefor prevent an endless loop. As an alternative, you can do a loop through group members, which is a more stable way to send commands to all items. I did this in the second rule. As It will take some time to set all Items, the lock is set to false a second later, just to be sure.

You don’t need more than one trigger for the rule.
You don’t need more than one rule for ON and OFF :wink:

It’s better to use the method myItem.sendCommand(status) instead of the action sendCommand(myItem,status) as the latter needs two strings as parameter, where the former allows to use status, string, number (when Item is of type number), …

1 Like

Good points as always, Udo! I should have provided more context for why I use the virtual item. I like having an indicator on my sitemap that tells me what mode my house is in, and it also enables me to activate it at any time using Google Home. As well, I use it as a check condition to determine if other rules should run. For example, my motion sensor turns on the kitchen lights at 30% if Night Mode is on.

1 Like

Thanks so far. I will try that :slight_smile:

For me it looks like it won’t work:

bMyLock = true
if(!bMyLock){}

Isn’t that biting each other? :wink:
I mean “if(!bMyLock){}” right after setting bMyLock to true won’t ever fire, right?

1 Like

Yep, I think you need to reverse those lines; test first, if the lock is free then set it as quickly as you can, and then do some work.

There is a “proper” locking mechanism in rules, but unless you want to queue (don’t think you do) then it’s probably not needed here.

1 Like

Aside of a couple wrongly set dots and the two lines which should be reversed, this is it :slight_smile:

Here the dots moved:

LightsInNightModeOFF.members.forEach[i | i.sendCommand(OFF)]
LightsInNightModeON.members.forEach[i | i.sendCommand(ON)]

Thanks Udo, I have learned a lot from your example.
Thanks to everyone else as well :slight_smile:

Sorry for typos :wink: never write rules in a hurry… :smiley:

I edited the previous posting…

1 Like

The point of the “proper” locking mechanism is, it won’t work here.

Afaik when using lock.lock, and try, the locked rule will do it’s job as soon as unlocked. see the excellent statement from @rlkoshak: Explain lock() command?

But here it should just ignore all sendCommands() for the members as long as the rule is executed.

almost right :smiley:
isendCommand(OFF) still misses a dot

1 Like

The proper reentrant lock does include a tryLock method - you may then choose to abandon/skip processing rather than queue for the lock.

I don’t see any practical advantage here, as you never do want to queue, so you don’t need reentrant lock to manage the queue.