I now added group logic to deploy the same approach in different rooms.
Here’s what I changed:
// Group for al triple-toggle items
Group gTripleToggle
// Triple-toggle group: all living room lights
Group gTripleToggle_LivingDining (gTripleToggle)
// Triple-toggle group: all living room lights
Group gTripleToggle_MasterBedroom (gTripleToggle)
I then assigned the relevant _Toggle
items to the related gTripleToggle
group, as in:
Switch GF_LivingDining_Uplighters_Light_Toggle "Uplighters" <light> (GF_LivingDining, gLight, gTradfriLight, gTripleToggle_LivingDining) {
channel="tradfri:0100:gwabcdefabcdef:65537:brightness,tradfri:0100:gwabcdefabcdef:65551:brightness,tradfri:0100:gwabcdefabcdef:65539:brightness,tradfri:0100:gwabcdefabcdef:65540:brightness"
}
Now the rules read as follows:
// import org.eclipse.smarthome.model.script.ScriptServiceUtil
import java.util.HashMap
val HashMap<String, Timer> timers = newHashMap
val HashMap<String, Integer> timer_counts = newHashMap
// Will contain the state at 1st toggle (will be the end state)
val HashMap<String, String> timer_states = newHashMap
// Will contain the gTripleToggle group
val HashMap<String, GroupItem> gTripleToggle_groupItems = newHashMap
var Timer timer_Tradfri_startup = null
var boolean STARTED_UP = false
rule "System startup - Tradfri TripleToggle"
when
System started
then
val String logTitle = "System startup - Tradfri TripleToggle"
val int STARTUP_DELAY_SECONDS = 10 // 30
logInfo(logTitle, "Initializing timers after {} seconds", STARTUP_DELAY_SECONDS)
STARTED_UP = false;
if (timer_Tradfri_startup === null) {
// Wait 5 seconds before resetting all timers
timer_Tradfri_startup = createTimer(now.plusSeconds(STARTUP_DELAY_SECONDS), [ |
STARTED_UP = true;
// Iterate over all group toggle items (by resolving the groups)
gTripleToggle.allMembers.forEach[ GenericItem toggleItem |
logInfo("gTripleToggle", "Checking group membership of TripleToggle item {}", toggleItem.name)
// For each item, iterate over the TripleToggle groups and check if the item belongs to one of these toggle groups
gTripleToggle.members.filter[ s | s instanceof GroupItem ].forEach[ GroupItem toggleGroup |
logDebug("gTripleToggle", "Checking if item {} belongs to TripleToggle Group {}", toggleItem.name, toggleGroup.name)
if ( toggleItem.getGroupNames.contains(toggleGroup.name) ) {
logInfo("gTripleToggle", "MACTH: item {} belongs to TripleToggle Group {}", toggleItem.name, toggleGroup.name)
gTripleToggle_groupItems.put(toggleItem.name, toggleGroup)
}
]
// Now initialize the timers
if (timers.get(toggleItem.name) !== null) {
timers.put(toggleItem.name, null)
}
]
timer_Tradfri_startup = null;
logInfo(logTitle, "Timers initialized")
])
} else {
timer_Tradfri_startup.reschedule(now.plusSeconds(STARTUP_DELAY_SECONDS))
}
end
rule "Member of gTripleToggle changed"
when
/* DOES NOT WORK (probably because it doesn't resolve subgroups):
Member of gTripleToggle changed
*/
Member of gTripleToggle_MasterBedroom changed
or Member of gTripleToggle_LivingDining changed
then
val String logTitle = "Member of gTripleToggle changed"
val int MONITORING_TIME_SECONDS = 3 // Will reset each time a click has been received, so it is okay to keep it short
if (STARTED_UP !== true) {
logWarn(logTitle, "Not yet initialized - nothing to do")
} else {
// Started up - rule items have been initialized
if (triggeringItem === null) {
logWarn(logTitle, "TripleToggle - AT START OF RULE - TriggeringItem is null (after OH restart?) - nothing to do",
name, tripleToggleGroup.name, triggeringItem.state)
} else {
val String name = triggeringItem.name
val GroupItem tripleToggleGroup = gTripleToggle_groupItems.get(name)
if ( (triggeringItem.state == NULL) || (triggeringItem.state == UNDEF) ) {
logWarn(logTitle, "TripleToggle - AT START OF RULE - {} (in group {}) has state {} - nothing to do",
name, tripleToggleGroup.name, triggeringItem.state)
} else {
logDebug(logTitle, "TripleToggle - AT START OF RULE - {} (in group {}) has state {} - starting the logic",
name, tripleToggleGroup.name, triggeringItem.state)
if (timers.get(name) === null) {
// No timer running - start a timer:
logDebug(logTitle, "TripleToggle {} has state {} (will be the end state) - monitoring started for {} seconds", name, triggeringItem.state, MONITORING_TIME_SECONDS)
timers.put(name, createTimer(now.plusSeconds(MONITORING_TIME_SECONDS), [ |
logDebug(logTitle, "TripleToggle {} - monitoring ended after {} seconds of inactivity", name, MONITORING_TIME_SECONDS)
// Reset the counter and kill the timer when the timer expires
timer_counts.put(name, 0)
//timer_toggling.put(name, false)
timers.put(name, null)
]))
// Set the toggle count to 1
timer_counts.put(name, 1)
// Store the desired end state (current state of triggeringItem)
timer_states.put(name, triggeringItem.state.toString)
} else {
val int cnt = timer_counts.get(name) + 1
val String stateInfo = timer_states.get(name)
if (cnt >= 3) {
logInfo(logTitle, "TripleToggle {} (end state will be {}) toggle count: {} ≥ 3 -- Switching {} both bedside lights",
name, stateInfo, cnt, stateInfo)
gTripleToggle_groupItems.get(name).allMembers.forEach[s |
// Send the command
s.sendCommand(stateInfo)
// Cancel any running timer
timers.get(s.name)?.cancel
timers.put(s.name, null)
]
} else {
logDebug(logTitle, "{} (end state will be {}) toggle count: {} - rescheduling the counter (adding {} seconds)",
name, stateInfo, cnt, MONITORING_TIME_SECONDS)
timers.get(name).reschedule(now.plusSeconds(MONITORING_TIME_SECONDS))
timer_counts.put(name, cnt)
}
}
}
}
}
end
I was surprised why
Member of gTripleToggle changed
does not work. I could however understand that it relates to the groupItem.members()
versus groupItem.allMembers()
logic, but it’s a bit odd that in rules, groups are not resolved to the ‘leaves’ (the actual items, irrespective of group nesting).