Rules DSL conversion to JS Scripting

I had a rule that, when a member of the family changed rooms, would update the occupancy status of each room. The original rule was:

rule "Room Occupancy Updater" 
when
    Member of Adults changed
then
    gRoomOccupancy.members.forEach[ room |
        if (room.name == Chet.state.toString || room.name == Kathrin.state.toString) {
            room.sendCommand(ON)
        } else {
            room.sendCommand(OFF)
        }
    ]
end 

This was working fine, but I am slowly converting my rules to javascript, and this one was next on my list. It was challenging for me as I have no experience with javascript, but after some reading, googling, etc. I was able to rewrite it as shown below:

items.getItem("gRoomOccupancy").members.forEach(function (room) {
  let ChetK = items.getItem("Chet")
  let KatS = items.getItem("Kathrin")
  let location = actions.Semantics.getLocation(room.rawItem)
  if(location.name == ChetK.state || location.name == KatS.state) {
    room.sendCommand("ON")
  } 
  else { room.sendCommand("OFF")}})

I have been relying on examples from other people’s rules they have posted in the forum so I thought I would add my own in case it might be of help to someone.

1 Like

Thanks for posting.

I can think of one minor improvement (which would also apply to the original rule too).

Let’s make it so it only sends the ON command to the room that the person just entered instead of both rooms. That way, if for example Kathrin is in the TV room with the lights off, it doesn’t turn ON again when Chet enters the kitchen (assumes the light turns on in the TV room when you enter it).

I’m assuming this is a UI rule. There is an object passed in called event with information about what caused the rule to run. For a changed triggered rule we can use event.itemName to see which Item caused the rule to trigger. And since the state of the Item that triggers the rule is the name of the room Item we don’t even need to search for it with a loop.

The rule becomes:

items.getItem(event.itemState).sendCommand('ON');

But what about the old room? We still need to turn that one off. We have event.oldItemState for that.

items.getItem(event.itemState).sendCommand('ON');
items.getItem(event.oldItemState).sendCommand('OFF');

Now there can be some error cases to handle though. What if the Item changed from NULL or UNDEF? Do you still want to send the ON command? Let’s assume yes, but we don’t want to send the OFF command because that would cause an error.

items.getItem(event.itemState).sendCommand('ON');
if(event.oldItemState.toString() != 'NULL' && event.oldItemState.toString() != 'UNDEF') {
    items.getItem(event.oldItemState).sendCommand('OFF');
}

What if the Item changed to UNDEF or NULL? Add a condition script condition to the rule so it doesn’t run in that case.

!items.getItem(event.itemName).isUninitialized

But let’s say that we can’t use the state of the Item that triggered the rule as the Item name and we still need to search. We can use a filter instead.

items.getItem('gRoomOccupancy').members.filter(i => i.name == event.itemState.toString()).sendCommand('ON');

The => is a shorthand for creating an anonymous function. You can make the function as complicated as you need to do the filter (e.g. parse the Item’s name and check only part of it).

Thanks Rich for the improvements. I have one question though, what about the case where Kathrin leaves the room, but I stay? The room is still occupied.

Ah, yes, Always an edge case. We need to check to see for that case.

// turn on the new room
items.getItem(event.itemState).sendCommand('ON');

// turn off the old room if it's now unoccupied.
var oldRoom = event.oldItemState.toString();
var occupancy = items.getItem('Adults').members.filter(i => i.state.toString() == oldRoom);
if(oldRoom != 'NULL' && oldRoom != 'UNDEF' && !occupancy.length) {
    items.getItem(event.oldItemState).sendCommand('OFF');
}

The filter will return all the members of ‘Adults’ who have the oldRoom as their state (indicating they are still there). Only if the oldRoom is known (not NULL or UNDEF) and unoccupied (the filter returned an empty array) will it be turned off.

1 Like

This topic was automatically closed 41 days after the last reply. New replies are no longer allowed.