Wall switch wonderland

wallc-s-e

In my previous post I explained how to connect Z-Wave (Plus) wall plugs to Openhab via MQTT with the help of mqttwarn and Z-Way server. Today is time to do the same with Z-Wave.me WALLC-S wall switches. Some people have been able to get these to work just fine, but I stumbled across to quite many posts where people have been having trouble configuring them especially in DIY installations, such as Openhab.

These too are Z-Wave Plus (Gen 5) devices that require SECURITY class functionality from Z-Wave server and thus there was no way to use them with the Z-Wave stack of Openhab. I did try to turn off the security functionality but in the end didn’t figure out how to do that. Nonetheless, the Z-way server from Z-Wave.me (that we hacked a bit in the previous post) works with these switches just fine. I like also the ability to decouple the handling of Z-Wave communication from Openhab and use MQTT as the interface for as many devices as possible.

WALLC-S can act as a basic on/off switch, dimmer as well as a scene switch. I didn’t want to program any kind of scene handling to devices but instead let the Openhab handle all the logic side, so dumb on/off and dimmer functionality is enough for my needs.

wallc-s-conf

After Z-Wave device inclusion we must first add the Z-Way server (more accurately, the Razberry) to WALLC-S’s control groups A and B (to receive notifications about button #1 and #3 presses) as well as to its “Life line” group (to receive update and battery status messages). Note that we must associate each WALLC-S to unique instance of Razberry to differentiate between switches. So for WALLC-S #1 (and #2), it is added to instance #1 (and #2 respectively) of control groups A and B.

Then we have to configure the button behaviour. I have a single paddle on both of the switches, so I set the switch in a pair mode (uppermost buttons 1 and 2 work in pairs, as well as the lower row buttons 3 and 4). Actually separate mode would work just as well, since we are not interested in the control group C and D messages.

Finally we configure for both control groups the command type which is sent to Razberry, in this case “Switch On/Off and Dim”.

Another Z-Way JSON debugging session…

Now when buttons #1 and #3 of WALLC-S #1 are pressed, the following data structures are updated:

[2015-07-11 01:24:02.944] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.srcNodeId = 22 (0x00000016)
[2015-07-11 01:24:02.945] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 01:24:02.945] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.level = 255 (0x000000ff)
.
.
[2015-07-11 01:25:24.109] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.srcNodeId = 22 (0x00000016)
[2015-07-11 01:25:24.109] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 01:25:24.110] [D] [zway] SETDATA devices.1.instances.1.commandClasses.32.data.level = 0 (0x00000000)

So command class 32 level is 255 for button #1 press and 0 for button #3 press.

Battery levels are received like this:

[2015-07-11 01:31:40.961] [D] [zway] SETDATA devices.22.data.lastReceived = 0 (0x00000000)
[2015-07-11 01:31:40.961] [D] [zway] SETDATA devices.22.instances.0.commandClasses.128.data.history.96 = 1436567500 (0x55a047cc)
[2015-07-11 01:31:40.962] [D] [zway] SETDATA devices.22.instances.0.commandClasses.128.data.last = 96 (0x00000060)

When button #1 is kept pressed (dimmer up)..

[2015-07-11 15:21:11.145] [D] [zway] SETDATA devices.19.data.lastReceived = 0 (0x00000000)
[2015-07-11 15:21:11.146] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcNodeId = 19 (0x00000013)
[2015-07-11 15:21:11.146] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 15:21:11.146] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.startChange = True

..and released:

[2015-07-11 15:21:12.650] [D] [zway] SETDATA devices.19.data.lastReceived = 0 (0x00000000)
[2015-07-11 15:21:12.650] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcNodeId = 19 (0x00000013)
[2015-07-11 15:21:12.650] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 15:21:12.650] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.stopChange = Empty

Similarily, button #3 is kept pressed (dimmer down)..

[2015-07-11 15:25:11.367] [D] [zway] SETDATA devices.19.data.lastReceived = 0 (0x00000000)
[2015-07-11 15:25:11.368] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcNodeId = 19 (0x00000013)
[2015-07-11 15:25:11.368] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 15:25:11.368] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.startChange = False

..and released:

[2015-07-11 15:25:12.440] [D] [zway] SETDATA devices.19.data.lastReceived = 0 (0x00000000)
[2015-07-11 15:25:12.440] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcNodeId = 19 (0x00000013)
[2015-07-11 15:25:12.440] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.srcInstanceId = 0 (0x00000000)
[2015-07-11 15:25:12.440] [D] [zway] SETDATA devices.1.instances.2.commandClasses.38.data.stopChange = Empty

So the “StopChange” indicates button release and “StartChange” is true for dimmer up and false for dimmer down. Note that the actual values will be lingering in the JSON tree long after buttons have been released and StopChange doesn’t seem to ever change its value, but only the updateTime tag is updated. Nonetheless, we don’t have to check for changes in values but just bind to the JSON tree to get notifications when the tags have been updated, just like in previous post.

I found out that Z-Way server holds no functionality to contain dimmer value nor there is any way to automatically get notifications on certain intervals that the button is still kept pressed, so we have to implement our on notification mechanism with timers in JavaScript. For the actual dimmer value (e.g. 0-100%), we let Openhab take care of that.

Hooking Z-Way server…

The amount of our extra code in main.js of Z-Way server is getting bigger (see the previous post), so we move it to separate file (mqtt.js) and just put this in the end of main.js:

    executeFile("mqtt.js");

In mqtt.js we define (in addition to Wall Plug code explained in the previous post)


// Here the id:19 and id:22 are hardcoded Z-Wave device ID's. Change them (and associated instance IDs) accordingly to your setup
var dimmers = [];
dimmers.push( { id:19, instance:1, timer:null, timercount:0 } );
dimmers.push( { id:22, instance:2, timer:null, timercount:0 } );

var dimmer_publish_interval = 100;  // in milliseconds

function getById(id, myArray) {
        for ( var i=0; i<myArray.length; i++) {
                if(myArray[i].id == id) {
                         return myArray[i];
                }
        }
        return null;
}

function battery_level_publish (device, theValue) {
        eventString = 'Device' + device + "/battery";
        publish_mqtt(eventString, theValue);
}

function wallswitch_binary (device, theValue) {
        eventString = 'Device' + device + "/wallswitch/binary";
        state = 'on';
        if (theValue == false){
                state = 'off';
        }
        publish_mqtt(eventString, state);
}

function wallswitch_dimmer_publish (device, theValue) {
        eventString = 'Device' + device + "/wallswitch/dimmer";
        state = 'increase';
        if (theValue == false){
                state = 'decrease';
        }
        publish_mqtt(eventString, state);
}

function wallswitch_dimmer_start (device, theValue) {
        wallswitch_dimmer_publish (device, theValue);
        dimmer = getById(device, dimmers);
        if (dimmer != null)
        {
                if (dimmer.timer != null) {
                        clearInterval(dimmer.timer);
                }
                dimmer.timercount = 0;
                dimmer.timer = setInterval(
                        function() {
                                wallswitch_dimmer_publish (device, theValue);
                                dimmer.timercount++;
                                if (dimmer.timercount>20) {
                                        // this is to stop sending updates eventually if for some reason the "dimmer_stop" 
                                        // message is not received and the dimmer gets "stuck"
                                        clearInterval(dimmer.timer);
                                }
                        }, dimmer_publish_interval);
        } else
        {
                console.log("dimmer not found!");
        }
}


function wallswitch_dimmer_stop (device, theValue) {
        dimmer = getById(device, dimmers);
        if (dimmer != null)
        {
                if (dimmer.timer != null)
                        clearInterval(dimmer.timer);
        }
}

// Binding to WALLC-S devices
for (var i=0; i < dimmers.length; i++) {

        var id = dimmers[i].id;
        (function(devid) {
                console.log("MQTT plugin: Configure dimmer " + devid);
                zway.devices[1].instances[ dimmers[i].instance ].commandClasses[38].data.startChange.bind(function() {
                        wallswitch_dimmer_start ( devid, this.value);
                });
                zway.devices[1].instances[ dimmers[i].instance ].commandClasses[38].data.stopChange.bind(function() {
                    wallswitch_dimmer_stop( devid, this.value);
                 });
                zway.devices[1].instances[ dimmers[i].instance ] .commandClasses[32].data.level.bind(function() {
                    console.log("MQTT plugin: wallswitch #" + devid + ": binary " + this.value);
                    wallswitch_binary (devid, this.value);
                 });
                zway.devices[ dimmers[i].id ].instances[0].commandClasses[128].data.last.bind(function() {
                    battery_level_publish (devid, this.value);
                 });
        })(id); // tie device ID so it is referenced correctly from callback funcs
}


And when testing the following MQTT messages are seen:

home/zwave/Device22/wallswitch/binary on
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer increase
home/zwave/Device22/wallswitch/dimmer decrease
home/zwave/Device22/wallswitch/dimmer decrease
home/zwave/Device22/wallswitch/dimmer decrease
home/zwave/Device22/wallswitch/dimmer decrease
home/zwave/Device22/wallswitch/dimmer decrease
home/zwave/Device22/wallswitch/binary off
home/zwave/Device22/battery 96

It works! 🙂

Openhab configuration

We can then define the items in .items file..

Switch Kitchen_Light_Switch "Kitchen light switch" (GF_Kitchen)  {mqtt="<[mosquitto:home/zwave/Device19/wallswitch/binary:state:MAP(wallswitchFromMqtt.map)]"}
String Kitchen_Light_Dimmer "Kitchen light switch" (GF_Kitchen)  {mqtt="<[mosquitto:home/zwave/Device19/wallswitch/dimmer:state:MAP(wallswitchFromMqtt.map)]"}
Number Kitchen_Light_Switch_Battery "Kitchen light switch battery [%3f %]" (gBattery,GF_Kitchen) {mqtt="<[mosquitto:home/zwave/Device22/battery:state:default"}

And add appropriate rules in .rules file:

var Integer manualTimeOut = 1800

rule "Kitchen Light Switch"
        when
        Item Kitchen_Light_Switch received update
        then
{
    logInfo("Kitchen Light Switch", "button changed state ("+Kitchen_Light_Switch.state +")")
    if (Kitchen_Light_Switch.state==ON)
        {
        sendCommand(Kitchen_Light_Toggle, ON)  
        sendCommand(Kitchen_Light_CT_Dimm, Color_Temperature.state as DecimalType)
        if (KitchenTimer == null )
            {                       
            // create timer
            logInfo("Kitchen Light Switch", " creating timer for " + manualTimeOut + "sec" )
                        
            KitchenTimer = createTimer( now.plusSeconds(manualTimeOut) )
                [ 
                logInfo("Kitchen Light Switch", "timer expired, switching off ")
                sendCommand(Kitchen_Light_Toggle,OFF)       
                KitchenTimer=null
                ]               
            }
        else
            {
                logInfo("Kitchen Light Switch", " rescheduling timer" )
                KitchenTimer.reschedule(now.plusSeconds(manualTimeOut))
            }
        }
    else
        {
        sendCommand(Kitchen_Light_Toggle, OFF)                  
        if (KitchenTimer != null )
            KitchenTimer.cancel
        KitchenTimer=null
        }
}
end

rule "Kitchen Light Dimmer"
    when
        Item Kitchen_Light_Dimmer received update
    then
{
    logInfo("Kitchen Light Dimmer", "changed state ("+Kitchen_Light_Dimmer.state +")")
    if (Kitchen_Light_Dimmer.state=="INCREASE")
        {
        sendCommand(Kitchen_Light_Dimm, INCREASE)
        }
    else
        {
        sendCommand(Kitchen_Light_Dimm, DECREASE)
        }
}
end

Also add the transformation map wallswitchFromMqtt.map

on=ON
off=OFF
increase=INCREASE
decrease=DECREASE

With this configuration the setup works nicely, except that there's 500-600 millisecond delay between a button press and a change in lighting, most of which can be accounted to Z-Wave (delay inside WALLC-S, Z-Wave transceiver, decoding and Z-Way server). When our JSON binding callback is called, a MQTT message is sent nearly instantaneously and Philips Hue lights react also quite quickly. I have to look into it more deeply in the future whether there's any way to speed up it a bit.

Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *