Group members as a blacklist

Hi there,
I have been writing rules which essentially perform diagnostics on the devices in each room, checking for IP connectivity, temperature states (fridges and freezers) for example.
I am really happy how this is progressing and now need to tackle a couple of issues that are not quite as easy for me to solve with my current knowledge base.

This one is bout items that have been removed from the system for example in winter I remove the fans from all the rooms and store them in the garage awaiting the next spring/summer time to come around.

Rather than the diagnostics always announce that the Fan is offline I would like to place the fan in a blacklist so that it would not be announced.

I thought about a whitelist approach but prefer a whitelist per room and a global blacklist.

I have created a blacklist that is called gDiags_Blklist but I am no quite sure how to check if _Online == OFF devices are a member of the blacklist group and if so do not include them in the offline announcements.

rule "Office Diagss"
when
	Member of EchoCmds changed to "office diagnostics"	
then
	logInfo("diagnostics","Trigger: " + triggeringItem.name)
	val sourceRoom = triggeringItem.name.split("_").get(0)
	val _TTS = sourceRoom+"_Echo_TTS"
	var String message = "All devices are working within specifications."
	var	String offlineDevices = "none"

	// loop through all profiles (all members of group 'gHO_Diags')
	logInfo("diagnostics", "Room: Office")
    gHO_Diags.members.forEach[ profile |
		var _Online = gHO_Diags.members.findFirst[ online | online.name == profile.name ]
		var _item = profile.name.split("_").get(1)
		logInfo("diagnostics"," item Name: "+_Online.name)
		if (_Online.state ==ON ) {
			logInfo("diagnostics"," item Online: "+_item)			
		} else {
			logWarn("diagnostics"," item Offline: "+_item)
			if (offlineDevices=="none") {
				offlineDevices = _item.toString+", "
			} else {
				offlineDevices = offlineDevices + _item.toString+", "
			}
		}
    ]

	if (offlineDevices!="none") { message = "The following devices were offline. "+offlineDevices }
	createTimer(now.plusSeconds(1)) [ | {_TTS.sendCommand(message)}]
	logInfo("diagnostics","Completed: "+message)
end

Thanks

Paul

On phone so bare with me.

In rule check for if blacklist.items.tostring.contains(thisitem.name) then ignore.

You could als9 use findfirst and find the item in the blacklist group. If not null then its been blacklisted.

Hope that help.

The code you have confuses me a bit. You are iterating through all members of gH0_Diags. For each member, you are findFirst the Item that has the same name as the profile.name and storing that in the variable _Online.

No two Items can ever have the same name. Therefore _Online will always be the same as profile. So that line is essentially doing nothing. You could use:

gH0_Diags.forEach[ _Online |
    var _item = _Online.name.split("_").get(1)

And have the exact same behavior.

But there are some even better things you can do here to simplify the code. For example, let’s just ignore the Items that are online so all we have to deal with are those that are not online.

gH0_Diags.members.filter[ online | online.state != ON].forEach[ online |
    var _item = online.name.split("_").get(1)
    logWarn("diagnostics", " item Offline: " +_item)
    offlineDevices = if(offlineDevices=="none") _item.toString+", " else offlineDevices + _item.toString+", "
]

NOTE: I replaced the five line if/else with the trinary operator that does the same thing in one line.

Now, to answer your original question, we just need to check the Group membership of each Item and filter out those that are a member of the blacklist Group.

gH0_Diags.members.filter[ online | !online.getGroupNames.contains("gDiags_Blklist") && online.state != ON].forEach[ online |
    var _item = online.name.split("_").get(1)
    logWarn("diagnostics", " item Offline: " +_item)
    offlineDevices = if(offlineDevices=="none") _item.toString+", " else offlineDevices + _item.toString+", "
]

This will filter out all the Items that are not ON and not a member of of the blacklist Group.

Now let’s move to the next step. We can use map/reduce to replace the body of the forEach since all you are doing is collecting parts of the names of the Items that don’t get filtered out.

val offlineDevices = gH0_Diags.members
  .filter[ online | !online.getGroupNames.contains("gDiags_Blklist") && online.state != ON]
  .map[name.split("_").get(1)+", "]
  .reduce[lst, _item | lst + _item]

if(offlineDevices !== null) {
    val message = "The following devices are offline. " + offlineDevices
    createTimer(now.plusSeconds(1), [ | _TTS.sendCommand(message)] // why the timer?
}

If you really wanted to you could move things around and add the warning log statement to the reduce, but since you are turning around and logging out the full list anyway I don’t see a need to do so.

The filter returns a list of only those Items that are not a member of the blacklist Group and whose state is not ON. The map returns a list of each of those filtered Item’s name, parsed to get the (1) part of the name and append the ", ". Finally, the reduce concatenates the result of the map into one String.

1 Like

A lot of stuff to digest.
I will work my way through your corrections and get back to this thread when I have the outcome, thanks Rich.

Regards
Paul

I am now questioning my whole approach, and considering a direction change :frowning:

I thought I would respond to the question left in the code proposed by Rich.

It allows enough time for the Amazon Echo to have said ‘Checking’ which is a routine I setup for all the different phrases I use to initiate diagnostics. Rather than her being cut off short when the results flood in from OH.

Now back to pondering…

Paul

As long as there is a reason I’m good with the timer. That’s not just a reason, it’s that most rare of beasts, a good reason. :grin:

1 Like