Window Covering - Unable to parse response


(Jack) #1

Hi,
I have a ZigBee device which supports the window covering cluster. When Smartthings sends a stop command to the device, the device sends a default response which the Smartthings hub receives. I see that the Smartthings hub returns Succes upon reception of the default reponse. However, in the Smartthings logs, it shows that it is unable the parse the reponse as seen in the below image. Why is this happening?

My logs for the ZigBee messages over the air between the ZigBee device and hub are shown below.


(Zach Varberg) #2

What DTH are you using? In general that message simply means that we didn’t generate an event from the message. In this case, that’s fine, as we generally don’t generate events from default responses. Of course if you don’t want that message to show, or would like it to generate an event, you can change that from the DTH.


(Jack) #3

This is my DTH. zigbee.command(0x0102, 0x02) is what sends the stop command.

metadata {
    definition (name: "Curtain", namespace: "Tek", author: "Jack", ocfDeviceType: "oic.d.switch", runLocally: false, minHubCoreVersion: '000.019.00012', executeCommandsLocally: false) {
        capability "Actuator"
        capability "Configuration"
        capability "Refresh"
        capability "Switch"
        capability "Health Check"
        
        command "pause"
        command "cont"

        fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006, 0102", deviceJoinName: "Window", manufacturer: "Tek",  model: "MD002"
    }

    // simulator metadata
    simulator {
        // status messages
        status "on": "on/off: 1"
        status "off": "on/off: 0"

        // reply messages
        reply "zcl on-off on": "on/off: 1"
        reply "zcl on-off off": "on/off: 0"
    }

    tiles(scale: 2) {
        multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
            tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
            	attributeState "on", label:'Open', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
                attributeState "off", label:'Closed', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
                attributeState "turningOn", label:'Opening', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#00A0DC", nextState:"turningOff"
                attributeState "turningOff", label:'Closing', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
            }
        }
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
        }        
        standardTile("contPuase", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
        	state "pause", label:'', icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc", nextState: "cont"
            state "cont", label:'', icon:'st.sonos.play-btn', action:'cont', backgroundColor:"#90d2a7", nextState: "pause"
        }
        
        main "switch"
        details(["switch", "contPuase", "refresh"])
    }
}

// Parse incoming device messages to generate events
def parse(String description) {
    log.debug "description is $description"
    def event = zigbee.getEvent(description)
    if (event) {
        sendEvent(event)
    }
    else {
        log.warn "DID NOT PARSE MESSAGE for description : $description"
        log.debug descMap
    }
}

def off() {
    zigbee.off()
}

def on() {
    zigbee.on()
}

def pause() {
    zigbee.command(0x0102, 0x02)
}

def cont() {
    zigbee.command(0x0102, 0x02)   // TODO: make change here
}

/**
 * PING is used by Device-Watch in attempt to reach the Device
 * */
def ping() {
    return refresh()
}

def refresh() {
    zigbee.onOffRefresh() + //zigbee.onOffConfig() +     
}

def configure() {
    // Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time)
    sendEvent(name: "checkInterval", value: 2 * 10 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
    log.debug "Configuring Reporting and Bindings."
    refresh()
}

(Zach Varberg) #4

Yeah, so you are printing that warn message yourself. The segment:

def event = zigbee.getEvent(description)
if (event) {
    sendEvent(event)
}
else {
    log.warn "DID NOT PARSE MESSAGE for description : $description"
    log.debug descMap
}

This passes the message off to our provided Zigbee library which will generate an event if it’s a recognizable message (i.e. a light turning on, or a temperature reading, etc). Then if it does generate an event, you pass it on from the DTH, but if it doesn’t you print that message. Since we don’t turn the default responses into events, it is hitting that else statement. That is not necessarily a bad thing as we don’t need to turn every message into an event. But you could change that logic if you don’t want that log message, or you could add custom handling yourself if you want to generate an event from that message.


(Jack) #5

How come whenever my device sends an on/off response, the warning message doesn’t show up unlike the stop response?


(Zach Varberg) #6

That’s because, for legacy reasons, we do generate on/off events from default responses on that cluster (On Off cluster 0x0006). This is particularly to deal with devices like ZLL bulbs which won’t report the on/off state change, but we want to reflect right away that the device turned on. So for that case, the call to zigbee.getEvent does generate an event, so it doesn’t hit the log statement.


(Jack) #7

Is there a way I can write my own code to generate a stop event from the default response?


(Zach Varberg) #8

You bet! Take a look here. In the else block it uses the parsed structure (you call it descMap) to check various parts of the message (e.g. the cluster and command) and from that generates an event. In this case you’d want to do something similar. If it is the cluster you are interested in, and the command is the default response command, and the first byte of the data field is a success byte, then you would use sendEvent to send whatever event you want.


(Jack) #9

Thanks. I have added the below code to generate the stop event from the default response below. My goal now is to have it so when I send a stop command, the app will flip between the ‘Pause’ state and the ‘Cont’ state in my ‘contPause’ standardTile of my DTH. However, when I try the code below, it doesn’t update my ‘contPause’ standardTile. How can I fix this?

    else if (descMap.clusterInt == CLUSTER_WINDOW) {
    	event.name = "contPause"
        log.debug "Stop Command Parsed"
        sendEvent(event)
    }

    standardTile("contPuase", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
    	state "pause", label:'', icon:'st.sonos.pause-btn', action:'pause', backgroundColor:"#cccccc", nextState: "cont"
        state "cont", label:'', icon:'st.sonos.play-btn', action:'cont', backgroundColor:"#90d2a7", nextState: "pause"
    }

(Jack) #10

Why doesn’t my standardTile update?


(PPO16) #11

because you have a typo: standardTile(“contPuase”,

:wink:


(Jack) #12

I fixed that, but I still have the problem.


(PPO16) #13

I think this is because you end-up in a loop: once the state “pause” is triggered by the first sendEvent if it is fired, the “pause” nextState event is sent right away to “cont” it which fires right away the “pause” nextState again and so on.

So either you remove the nextState and have to give the value:“cont” or value:“pause” during the SendEvent, or you have to manage the toggle outside of the tile like I do here.

Just my 2 cents