Design Pattern: Switch & Dimmer

Hi, the following code should be working now, but as I moved away from the enocean rocker switches towards Homematic switches, I am no longer able to test it:

import java.util.Map
import org.eclipse.smarthome.model.script.ScriptServiceUtil
import org.eclipse.smarthome.core.thing.events.ChannelTriggeredEvent

val Map<String, Timer> Counters = newHashMap
val Map<String, Timer> Timers = newHashMap
val Map<String, DateTime> Timestamps = newHashMap

// ========================================================================= //

val incrementCounter = [ String id, boolean resetCounter, Map<String, Timer> Counters |
  val item = ScriptServiceUtil.getItemRegistry.getItem(id + "Count")
  val count = (item.state as Number).intValue + 1
  postUpdate(id + "Count", String::format("%d", count))

  if (resetCounter) {
    // Deactivate the timer (if running)
    Counters.get(id)?.cancel()
    Counters.remove(id)

    val offset = now.plusSeconds(10)
    Counters.put(id, createTimer(offset, [|
      postUpdate(id + "Count", "0")
    ]))
  }
]

// ========================================================================= //

val simpleSwitch = [ ChannelTriggeredEvent receivedEvent, String id, boolean resetCounter, Map<String, Timer> Counters, Map<String, Timer> Timers, Map<String, DateTime> Timestamps |
  if (receivedEvent.event.endsWith("PRESSED")) {
    incrementCounter.apply(id, resetCounter, Counters)
    sendCommand(id, "ON")
  }
  if (receivedEvent.event.endsWith("RELEASED")) {
    sendCommand(id, "OFF")
  }
]

val doubleSwitch = [ ChannelTriggeredEvent receivedEvent, String id, boolean resetCounter, Map<String, Timer> Counters, Map<String, Timer> Timers, Map<String, DateTime> Timestamps |
  if (receivedEvent.event.endsWith("PRESSED")) {
    Timestamps.put(id, now)
    sendCommand(id, "ON")
  }
  if (receivedEvent.event.endsWith("RELEASED")) {
    // Get timestamp of switch pressed down
    var pressed = Timestamps.get(id)
    Timestamps.remove(id)

    // Handle short or long press
    val offset = pressed?.plusMillis(700)
    if (now.isBefore(offset)) {
      incrementCounter.apply(id, resetCounter, Counters)
      sendCommand(id + "ShortPress", "ON")
      sendCommand(id + "ShortPress", "OFF")
    } else {
      sendCommand(id + "LongPress", "ON")
      sendCommand(id + "LongPress", "OFF")
    }
    sendCommand(id, "OFF")
  }
]

val dimmerSwitch = [ ChannelTriggeredEvent receivedEvent, String id, boolean resetCounter, Map<String, Timer> Counters, Map<String, Timer> Timers, Map<String, DateTime> Timestamps |
  if (receivedEvent.event.endsWith("PRESSED")) {
    Timestamps.put(id, now)
    sendCommand(id, "ON")

    // Start timer if switch is held down
    val offset = now.plusMillis(700)
    Timers.put(id, createTimer(offset, [|
      // Handle long press
      sendCommand(id + "LongPress", "ON")
      sendCommand(id + "LongPress", "OFF")

      // Reschedule timer for this switch, but max. 10 times
      if (now.isBefore(Timestamps.get(id)?.plusSeconds(7))) {
        val newoffset = now.plusMillis(700)
        Timers.get(id)?.reschedule(newoffset)
      }
    ]))
  }
  if (receivedEvent.event.endsWith("RELEASED")) {
    // Deactivate the timer (if running)
    Timers.get(id)?.cancel()
    Timers.remove(id)
  
    // Get timestamp of switch pressed down
    var pressed = Timestamps.get(id)
    Timestamps.remove(id)

    // Handle short or long press
    val offset = pressed?.plusMillis(700)
    if (now.isBefore(offset)) {
      incrementCounter.apply(id, resetCounter, Counters)
      sendCommand(id + "ShortPress", "ON")
      sendCommand(id + "ShortPress", "OFF")
    }
    
    sendCommand(id, "OFF")
  }
]

// ========================================================================= //

rule "Configuration Button 1"
when
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchA" triggered DIR1_PRESSED or
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchA" triggered DIR1_RELEASED
then
  simpleSwitch.apply(receivedEvent, "Button_1", false, Counters, Timers, Timestamps)
end

rule "Configuration Button 2"
when
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchB" triggered DIR1_PRESSED or
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchB" triggered DIR1_RELEASED
then
  doubleSwitch.apply(receivedEvent, "Button_2", false, Counters, Timers, Timestamps)
end

rule "Configuration Button 3"
when
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchA" triggered DIR2_PRESSED or
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchA" triggered DIR2_RELEASED
then
  dimmerSwitch.apply(receivedEvent, "Button_3", false, Counters, Timers, Timestamps)
end

rule "Configuration of Button 4"
when
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchB" triggered DIR2_PRESSED or
  Channel "enocean:rockerSwitch:FT1I6L57:0035FA61:rockerswitchB" triggered DIR2_RELEASED
then
  dimmerSwitch.apply(receivedEvent, "Button_4", true, Counters, Timers, Timestamps)
end

Please note that I remove the “maxCount” variable due to a limitation of the rule dsl that allows a max. of 6 parameters for lambdas. The counter will increment to infinity until it will be reset (if resetCounter boolean was set to true). Otherwise, I suggest that the rule that handles the profiles and reads the Counter Item should reset it, if it exceeds the last known profile id. :slight_smile:

The latency is quite ok. For sure you have a delay for the long press which is a natural thing, as the script needs to wait for the given time until it can detect the long press. I didnt feel like it was a negativ effect.

So give it a try and let us know how it works :slight_smile:

Best Bechte

1 Like

Hi guy’s.

I am brand new to OH3, I have many enocean switches and this is exactly what I was looking for (to dim Tradfri lamps with an enocean switch).

I also understand the code so far, the only question I have is where to put it in OH3 :pleading_face:

Please excuse the stupid question and many thanks for the help