[SOLVED] How do I debounce or antiflap notifications for many devices in a group?

This is called “antiflapping” or “debounce.” You can see an example in Generic Presence Detection and in [Deprecated] Design Pattern: Motion Sensor Timer. [Deprecated] Design Patterns: Generic Is Alive is actually exactly what you are looking for.

To answer your question, yes you can have one Rule that processes all of these events. But there is no way to get around the fact that you will need to create a separate Timer for each Item if you want to alert on each Item individually. Since you will want to potentially cancel that timer at a later time, you will need to keep a handle on that Timer which means the Timer either needs to be an Item using Design Pattern: Expire Binding Based Timers and Design Pattern: Associated Items or creating Timers in the Rule and storing them in a HashMap (ConcurrentHashMap if you are worried about thread safeness).

The advantage of using Expire binding is the Rule itself becomes very simple. You also have the ability to reset the Timer pretty easily on an OH restart. Personally I will always accept more Items in exchange for a simpler Rule.

import org.eclipse.smarthome.model.script.ScriptServiceUtil

rule "Network Device went offline"
when
    Member of gNetworkAlerts changed
then
    val timer = ScriptServiceUtil.getItemRegistry.getItem(triggeringItem.name+"_OfflineTimer") as SwitchItem

    // Device is now ON and we've already sent an offline alert
    if(triggeringItem.state == ON && timer.state == OFF) {
        // send "back online" alert
    }
    // We haven't alerted yet, start or cancel the timer as appropriate
    else {
        triggeringItem.postUpdate(if(triggeringItem.state == ON) OFF else ON)
    }
end

rule "Network device has been offline for too long"
when
    Member of gNetworkAlertsTimers received command OFF
then
    if(swAlerts_Network.state == ON){
        // send  offline alert
    }
end

The code above is actually a bit simpler than your existing code yet it does more.

If you want to use Timers then the code would look like:

import java.util.Map

val Map<String, Timer> timers = newConcurrentHashMap // I think this works, otherwise use new ConcurrentHashMap() and import it above

rule "Network device status changed"
when
    Member of gNetworkAlerts changed
then
    val timer = timers.get(triggeringItem.name)

    // only send an online alert if we've already sent an offline alert
    if(triggeringItem.state == ON && timer === null) {
        // send back online alert
    }
    // cancel the timer if it came back online before we sent an offline alert
    else triggeringItem.state == ON && timer !== null) {
        timer.cancel
        timers.put(triggeringItem.name, null)
    }
    // it went offline, set a timer to send an alert in a minute
    else {
        timers.put(triggeringItem.name, createTimer(now.plusSeconds(60), [ |
            // send offline alert
            timers.put(triggerintItem.name, null)
        ])
    }
end

There are thread safe lists that can be used. The main thread safety problems in Rules DSL revolve around global lambdas and doing operations on members of Groups when the members are actively changing their states. When creating your own lists or maps, you have the choice of thread safe versions. For something like this I’d be very surprised that one would see a concurrency problem more than once a year if a non-thread safe list or map were used.