February 28, 2017

I’m mainly looking at OpenHAB for support of the advanced functionality in the HomeSeer HS-WD100+ in-wall dimmers. Specifically, they have multi-tap functionality supporting four commands in addition to the generic On/Off function. For example, my pool light switch is out by the shed; wouldn’t be nice if I could just double-tap the switch at the back door instead?

Here’s one way of making it work in OpenHAB 2…

Out of the Box

OpenHAB fully recognizes the HomeSeer switches. When you include one into your network, you’ll get two channels: Dimmer and Scene Number. The Dimmer channel is self-explanatory; less obviously, however, you recognize multi-taps by looking at the Scene Number.

Put simply, the Scene Number is a floating point number with one of six possible values:

Scene #Meaning
1.0Top button pressed
1.3Top button double-tap
1.4Top button triple-tap
2.0Bottom button pressed
2.3Bottom button double-tap
2.4Bottom button triple-tap

Simple enough; you just examine the number, and you know what happened at the switch. So how do you make that work with OpenHAB?

Configuring An Item

First, you need to add an item for your switch’s Scene Number channel. I recommend doing this manually; the “simple linking” stuff in OH2 is nice, but you can’t name your items. To add it manually, you copy the channel name (zwave:device:9635f4cc:node2:scene_number in the example above) and paste it into an items file in a line that looks like this:

Number light_office_scene "Office Switch Scene" { channel="zwave:device:9635f4cc:node2:scene_number"  }

This item can then be used in your rules, both to detect updates and changes, and to simply see what the last action was.

Helpful Functions

It’s hard to keep the number-to-meaning mapping straight in your head, so let’s render that unnecessary. OpenHAB’s code reuse facilities are nearly non-existent, so we’ll hack around it using a stored lambda function. Just place this at the top of the rules file where you define your multi-tap actions:

import org.eclipse.xtext.xbase.lib.Functions.*
import org.eclipse.xtext.xbase.lib.Procedures.*

val Function5 hs100Scene = [
    NumberItem sceneId,
    Procedure0 doubleOn,
    Procedure0 doubleOff,
    Procedure0 tripleOn,
    Procedure0 tripleOff |

    switch(sceneId.state) {
    case 1.3: { if (doubleOn  != null) doubleOn.apply()  }
    case 2.3: { if (doubleOff != null) doubleOff.apply() }
    case 1.4: { if (tripleOn  != null) tripleOn.apply()  }
    case 2.4: { if (tripleOff != null) tripleOff.apply() }
    }
]

When called, this function will examine the supplied Scene Number object, and then call one (and ONLY one) of the supplied lambda functions. Which one depends on which action was detected. If you don’t want to support an action on a given switch, just pass null for that argument.

Sound Complicated? It may seem it, but wait ’til you see what it does for you…

Applying Scenes

Once you’ve defined your item and the above helper function, all you need to do is define a rule on the Scene Number item. For example, this rule currently exists in my system, in the same file as the above function:

rule "Office Scenes"
when
    Item light_office_scene received update
then
    hs100Scene.apply(light_office_scene,
        [| // double tap on
            sendCommand(gLivingRoom, OFF)
            sendCommand(light_office, ON)
        ],
        [| // double tap off
            sendCommand(gLivingRoom, ON)
            sendCommand(light_office, OFF)
        ],
        null, // no triple tap on
        null  // no triple tap off
    )
end

It should be fairly easy to tell what this does: if you double-tap the top of the paddle, it will turn the office lights on and the living room lights off. If you double-tap the bottom of the paddle, it does the reverse. Triple tap would simply be a matter of adding two more lambdas in place of the nulls – but I don’t really have any other use for the feature on that particular switch.

See? It’s easy!

Notes

Just a few closing thoughts:

  • Remember: you can’t use the hs100Scene function outside the rules file it was defined in.
  • You almost certainly want to use a “received update” trigger instead of a “changed” trigger. If you use “changed”, you’ll only receive events if the scene number is different from the last one it saw. This is a recipe for sketchy behavior in most circumstances.
  • This will likely work with the HS-WS100+ also, but I haven’t verified it.
  • I don’t know where the x.1 and x.2 scene numbers went. It’s a mystery!
  • I haven’t tinkered enough with lambdas in OpenHAB to know how the scoping works; you may or may not have issues accessing variables defined in the rule body. Consider it an experiment!
  • You could easily break this into four functions ( eg. onDoubleTap.apply(sceneObject, [| ... ]) ). This could make things more readable.

The single-file scope is the biggest issue I see with this. If OpenHAB would implement a way to define global helper functions, I would be far happier with the system. It is, so far, the one glaring limitation.

In the mean time, however, my goal is accomplished: the HomeSeer switches are fully usable! When my pool guys finish fixing the light (it’s an ongoing saga involving a leak and crappy electrical conduit – don’t ask), and as soon as HomeSeer has the appropriate switches back in stock, I’ll be able to control my pool light from the back door instead of traipsing out to the shed to flip the switch.

Handy, right?


Updates:
  • 2017/03/05: Fix a typo in the labmda code and a mental name collision in the article. Nothing you’d probably notice.