Zigbee Lock status handler

Hey!

I’m trying to make a handler that will show a status of a lock only
i have extracted this chainging values:
LOCKED: F08401010128F6FF42104D4C2101110B0B0B0B0B0B0B0B0B0B0B
UNLOCKED: F08401010128F6FF42104D4C210B000B0B0B0B0B0B0B0B0B0B0B

and determined to use 0x2101 as the locked cluster
and 0x210B as unlocked cluster

but i cant seem to figure out what command to type at the “def parse”
and “def lock” “def unlock”

could any one point me to the right direction?

Hi, can you give more details about the Zigbee device you’re trying to integrate?

Have you verified those clusters are supported by your device? Maybe through its technical specifications.

These values came from a ReadAttribute command?

no, from the IDE live logging, every time i lock \ unlock

Oh, so you already installed the device through the ST mobile App?
If so, which Device type is it using? You can get that value in the IDE > My devices > Select the device and it’s the third property.

I have created a brand new Zigbee Device Handler using a “lock” template from there i put the device model etc, and i use it as a handler for the lock but its not functioning because reading the “online guide” didnt help me to move forward

Ok, so that means your device is not currently supported by the ST platform and you are building your own device handler, correct?
Have you tried using another similar DTH but adding the device fingerprint? Eg.

Which online guide do you mean? I can help you clarify your doubts.

Correct, None of those locks are similar to mine, what are fingerprints and how do i extract them?
this “guide”: Overview — SmartThings Classic Developer Documentation

These are some of the basics about Device Handlers to help you have a better understanding.
A DTH is composed of the following sections:

  1. Definition/Metadata - This is where the main properties are set, eg. Name, Device Presentation, Capabilities, fingerprints (That document has the indications to generate it)
  2. Preferences - In this section, you can define some fields to allow the user to custom some configurations
  3. Functions (Parse and Events):
  • parse() is where all the events coming from the device are received.
    You can print the event description to see its content and how to convert it:
def parse(String description) {
   log.trace"parse description: $description"
}
  • lock() and unlock() are the commands that belong to the lock capability of SmartThings.
    To send these commands to the device, you should use:
zigbee.command(cluster-id, command-id)
  • configure() is called when the device is discovered by SmartThings. You can send configuration events using this structure:
zigbee.configureReporting(cluster-id, attribute-id, dataType-id, 
    minReportingInterval, maxReportingInterval, reporting-change)

Note: the tiles section was replaced by the usage of device presentations.

Thanks for the help, although I’m not quite understanding where to put all of these commands, I don’t want smartthings to send anything, just to get the lock status and display it the right way

Also please explain to me like I’m 5 year old, thank you for the patients:)

ok, right now, I believe your DTH already recognizes the device as it is reporting the lock and unlock events, right?

All Zigbee RX messages are received in parse(), this is why I suggest you print the description.
As your device is using manufacturer-specific clusters, there you should start parsing the message, for example, you can check the cluster this message belongs to:

//0x0500 should be replaced by the lock or unlock cluster ID
if (descMap?.clusterInt == 0x0500) 

Once you identify the command’s origin, you need to create an event map:

Map responseMap = [:]
def result = null
responseMap.name = "lock"
responseMap.value= "locked" // or unlocked
 // createEvent() sends the new value to the capability
result << createEvent(responseMap)
log.trace "ZigBee DTH - parseAttributeResponse() returning with result:- $result"

Note: Please, share your DTH to see its content so far.

im lost and my code is a mess, ill clean up if it will ever work:(

/**
 *  AQARA DOOR LOCK S2
 *
 *  Copyright 2021 ARTHUR V
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 */
metadata {
	definition (name: "AQARA DOOR LOCK S2", namespace: "AQARA S2", author: "ARTHUR V", cstHandler: true) {
		capability "Lock"
   //     capability "Battery"
        capability "Health Check"
        
        
        fingerprint endpointId: "01", profileId: "0104", deviceId: "F084", inClusters: "0000,0003,0001", outClusters: "0101", manufacturer: "LUMI", model: "lumi.lock.acn02", deviceJoinName: "AQARA Door Lock S2"
}


	simulator {
		status "lock": "on/off: 1"
		status "unlock": "on/off: 0"
	}

    tiles(scale: 2) {
        multiAttributeTile(name:"lock", type: "generic", width: 6, height: 4) {
            tileAttribute("device.lock", key: "PRIMARY_CONTROL") {
                attributeState "lock", label:"Locked"
                attributeState "unlock", label:"Unlocked"
            }
        }
        valueTile("battery", "device.battery", inactiveLabel: false) {
            state "battery", label:'${currentValue}%', unit:"%", icon:"https://raw.githubusercontent.com/bspranger/Xiaomi/master/images/XiaomiBattery.png",
            backgroundColors:[
                [value: 10, color: "#bc2323"],
                [value: 26, color: "#f1d801"],
                [value: 51, color: "#44b621"]
            ]
        }
        valueTile("lastcheckin", "device.lastCheckin", decoration: "flat", inactiveLabel: false, width: 4, height: 1) {
            state "default", label:'Last Event:\n${currentValue}'
        }
        valueTile("batteryRuntime", "device.batteryRuntime", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
            state "batteryRuntime", label:'Battery Changed:\n ${currentValue}'
        }

        main (["lock"])
        details(["lock","battery", "lastcheckin","batteryRuntime"])
   }
}

/*
private getCLUSTER_BELLRING() { 0x2300 }
private getCLUSTER_UNLOCKEDINSIDE() { 0x210B }
private getcluster_unlockedoutside() { 0x2111 }
private getcluster_unlockeduserfinger() { 0x2101 }
private getcluster_unlockedadminfinger() { 0x2301 }
private getcluster_unlockedpw() { 0x2302 }
private getcluster_locked() { 0x2101 }
private getcluster_latchout() { 0x2102 }
private getcluster_latchin() { 0x2103 }
private getcluster_fingerfail() { 0x2302 }
*/

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
	// TODO: handle 'lock' attribute

}

// handle commands
def lock() {

}

def unlock() {

}


Thanks for the information, is AQARA Door Lock S2 the device you’re trying to integrate?
So far, your DTH should recognize the device and I believe also receive the events in parse() which is only printing the event. According to what we’ve been discussing, here are the modifications you should apply:

  • To identify the events (you just need to verify the clusters IDs are correct)
  • installed(), configure() and updated() should be present as they’re part of the device’s lifecycle.
  • Add the corresponding IDs at inClusters and outClusters
/**
 *  AQARA DOOR LOCK S2
 *
 *  Copyright 2021 ARTHUR V
 *
 *  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
 *  in compliance with the License. You may obtain a copy of the License at:
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
 *  on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
 *  for the specific language governing permissions and limitations under the License.
 */
metadata {
	definition (name: "AQARA DOOR LOCK S2", namespace: "AQARA S2", author: "ARTHUR V", cstHandler: true) {
		capability "Lock"
   //     capability "Battery"
        capability "Health Check"
        
        
        fingerprint endpointId: "01", profileId: "0104", deviceId: "F084", inClusters: "0000,0003,0001,0101", outClusters: "0101", manufacturer: "LUMI", model: "lumi.lock.acn02", deviceJoinName: "AQARA Door Lock S2"
}


	simulator {
		status "lock": "on/off: 1"
		status "unlock": "on/off: 0"
	}

    tiles(scale: 2) { }
}

/*
private getCLUSTER_BELLRING() { 0x2300 }
private getCLUSTER_UNLOCKEDINSIDE() { 0x210B }
private getcluster_unlockedoutside() { 0x2111 }
private getcluster_unlockeduserfinger() { 0x2101 }
private getcluster_unlockedadminfinger() { 0x2301 }
private getcluster_unlockedpw() { 0x2302 }
private getcluster_locked() { 0x2101 }
private getcluster_latchout() { 0x2102 }
private getcluster_latchin() { 0x2103 }
private getcluster_fingerfail() { 0x2302 }
*/

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
    def result = null
    Map responseMap = [:]
	//it could be the ID 0xff11 or others
	if (descMap?.clusterInt == 0x0101 && descMap.attrInt == 0xff11){
    	// assign the corresponding values
    	responseMap.name = "lock"
        responseMap.value= "locked" //or unlocked
    } 
    
	result << createEvent(responseMap)
	log.trace "ZigBee DTH - parseAttributeResponse() returning with result:- $result"
}

def installed(){	
	log.trace "Device installed"
}

def configure(){
	state.configured = true
	// Device-Watch allows 2 check-in misses from device + ping (plus 2 min lag time)
    // This interval can me modified according to your requirements
	sendEvent(name: "checkInterval", value: 2 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
	
    // Cluster ID, Attribute ID and data type are information gotten from the device. (**These are only examples**)
	def cmds =
		zigbee.configureReporting( 0x0101 , 0xff11,
								  DataType.ENUM8, 0, 3600, null) +
		zigbee.configureReporting(0x0101 , 0xfff6,
								  DataType.UINT8, 0, 3600, null)

	return cmds
}

def updated(){	
    log.trace "Device updated"
}

// handle commands
def lock() {
	log.trace "ZigBee DTH - Executing lock() for device ${device.displayName}"
	def cmds = zigbee.command(0x0101, xxxxx)
	log.info "ZigBee DTH - lock() returning with cmds:- $cmds"
	return cmds
}

def unlock() {
	log.trace "ZigBee DTH - Executing lock() for device ${device.displayName}"
	def cmds = zigbee.command(0x0101, xxxx)
	log.info "ZigBee DTH - lock() returning with cmds:- $cmds"
	return cmds
}

yes

how?

those i gave you in the DTH are just ripped from another template, no clue how to fish them…

530fbb39-a003-4403-bea3-b19a193786cf 15:04:54: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:04:54: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C2111000B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C2111000B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:04:33: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:04:33: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C210B000B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C210B000B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:23: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:23: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C2100110B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C2100110B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:23: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:23: debug Parsing 'catchall: 0104 0000 01 01 0000 00 BDE6 00 01 115F 0A 01 01FF422C01215C0D03281805217D00082109410A21000064202665205D6620646720166823000006216920119B210000'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:17: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:17: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C2101110B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C2101110B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:15: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:15: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C2111000B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C2111000B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:14: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:14: debug Parsing 'read attr - raw: BDE60101012810FF42104D4C2301210005090909090909090909, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: ff10, result: success, encoding: 42, value: 4D4C2301210005090909090909090909'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:12: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:12: debug Parsing 'read attr - raw: BDE60101012811FF42104D4C2302000040090909090909090909, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: ff11, result: success, encoding: 42, value: 4D4C2302000040090909090909090909'
530fbb39-a003-4403-bea3-b19a193786cf 15:03:08: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:03:08: debug Parsing 'read attr - raw: BDE60101012811FF42104D4C2301000040090909090909090909, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: ff11, result: success, encoding: 42, value: 4D4C2301000040090909090909090909'
530fbb39-a003-4403-bea3-b19a193786cf 15:02:55: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:02:55: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C210B000B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C210B000B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:02:54: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:02:54: debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C2101110B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C2101110B0B0B0B0B0B0B0B0B0B0B'
530fbb39-a003-4403-bea3-b19a193786cf 15:02:52: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:02:52: debug Parsing 'read attr - raw: BDE601010128F2FF42104D4C20020C0C0C0C0C0C0C0C0C0C0C0C, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff2, result: success, encoding: 42, value: 4D4C20020C0C0C0C0C0C0C0C0C0C0C0C'
530fbb39-a003-4403-bea3-b19a193786cf 15:02:52: error java.lang.NullPointerException: Cannot invoke method leftShift() on null object @line 64 (parse)
530fbb39-a003-4403-bea3-b19a193786cf 15:02:52: debug Parsing 'read attr - raw: BDE601010128F1FF42104D4C20030C0C0C0C0C0C0C0C0C0C0C0C, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff1, result: success, encoding: 42, value: 4D4C20030C0C0C0C0C0C0C0C0C0C0C0C'
530fbb39-a003-4403-bea3-b19a193786cf 15:02:34: trace Device updated
530fbb39-a003-4403-bea3-b19a193786cf 15:02:33: trace Device installed

This info is in the logs from parse(), eg.

debug Parsing 'read attr - raw: BDE601010128F6FF42104D4C210B000B0B0B0B0B0B0B0B0B0B0B, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: fff6, result: success, encoding: 42, value: 4D4C210B000B0B0B0B0B0B0B0B0B0B0B'

debug Parsing 'read attr - raw: BDE60101012810FF42104D4C2301210005090909090909090909, dni: BDE6, endpoint: 01, cluster: 0101, size: 40, attrId: ff10, result: success, encoding: 42, value: 4D4C2301210005090909090909090909'
  • The Cluster ID is 0x0101
  • There are different Attribute IDs but you need to identify what each means depending on the events you’re sending from the device.

I corrected my sample to avoid the error of Cannot invoke method leftShift(). However, it won’t send the correct events if the conversion of the message received in parse() is incorrect.
I don’t have an AQARA Dooor Lock device so I cannot tell you exactly how to convert the messages, what you can do is:

  • To print logs along the parsing process to debug and verify it’s working as it should. For example, log.debug "Parsing '${description}'" prints the complete event
  • You can also use `log.trace “debug point ${variable}” to print the content of any variable you’re using

Take a look at the parsing method of the sample below and see if you can use some part of it: