4 button battery

I have a button device from LK (Danish manufacture under Schneider) with 4 buttons.
I've found some code which at first seemed to fit the bill, but needed some tweaking to get it to work.

The code is from a OSRAM 4 button switch which has 4 endpoints, like this button. But this button I have apparently only uses 1 endpoint, what I can tell at least. The weird thing is that I can't tell the difference between a button press/hold/release on button 1 and 3, or 2 and 4.

From the logs where I've pressed button 1 and 3:
[dev:776] 2021-07-13 19:09:45.712 [debug] Parse returned LK tryk button 1 was pushed
[dev:776] 2021-07-13 19:09:45.708 [debug] 6
[dev:776] 2021-07-13 19:09:45.704 [debug] parse description: catchall: 0104 0006 15 01 0040 00 40BE 01 00 0000 01 00
[dev:776] 2021-07-13 19:09:45.700 [debug] Parsing 'catchall: 0104 0006 15 01 0040 00 40BE 01 00 0000 01 00 '
[dev:776] 2021-07-13 19:09:44.267 [debug] Parse returned LK tryk button 1 was pushed
[dev:776] 2021-07-13 19:09:44.265 [debug] 6
[dev:776] 2021-07-13 19:09:44.261 [debug] parse description: catchall: 0104 0006 15 01 0040 00 40BE 01 00 0000 01 00
[dev:776] 2021-07-13 19:09:44.259 [debug] Parsing 'catchall: 0104 0006 15 01 0040 00 40BE 01 00 0000 01 00 '

Catchall is exactly the same.
Is there be some other way to detect the buttons? In the official gateway and software, it is totally possible, but how?

Show code
/**
 *  OSRAM 4 Button Switch Handler
 *
 *  Copyright 2017 AnotherUser
 *
 *  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.
 *
 * Modified from code written by  motley74 and sticks18.
 * Original source: https://github.com/motley74/SmartThingsPublic/blob/master/devicetypes/motley74/osram-lightify-dimming-switch.src/osram-lightify-dimming-switch.groovy
 */



metadata {
	definition (name: "DEV - OSRAM 4 Button Switch LK TEST", namespace: "AnotherUser", author: "AN") {

	capability "Actuator"
	capability "Battery"
	capability "PushableButton"
	capability "HoldableButton"
	capability "Configuration"
	capability "Refresh"
   
		fingerprint profileId: "0104", deviceId: "0810", inClusters: "0000, 0001, 0003, 0020, FF17", outClusters: "0003, 0004, 0005, 0006, 0008, 0019, 0102", manufacturer: "OSRAM", model: "Switch 4x EU-LIGHTIFY", deviceJoinName: "OSRAM 4x Switch"
	
	}
}

def installed() {
	configure()
}

// parse events into attributes
def parse(String description) {
	log.debug "Parsing '${description}'"
	// TODO: handle 'numberOfButtons' attribute
	// Parse incoming device messages to generate events

	Map map = [:]
	log.debug "parse description: $description"
	if (description?.startsWith('catchall:')) {
		// call parseCatchAllMessage to parse the catchall message received
		map = parseCatchAllMessage(description)
	} else if (description?.startsWith('read')) {
		// call parseReadMessage to parse the read message received
		map = parseReadMessage(description)
	} else {
		log.debug "Unknown message received: $description"
	}
  //return event unless map is not set
  return map ? createEvent(map) : null
}

def configure() {
  log.debug "Confuguring Reporting and Bindings."
  def configCmds = [
// Binding ON/OFF functions
	"zdo bind 0x${device.deviceNetworkId} 21 1 6 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 22 1 6 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 23 1 6 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 24 1 6 {${device.zigbeeId}} {}",
// Binding dimmer functions
	"zdo bind 0x${device.deviceNetworkId} 21 1 8 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 22 1 8 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 23 1 8 {${device.zigbeeId}} {}",
	"zdo bind 0x${device.deviceNetworkId} 24 1 8 {${device.zigbeeId}} {}",
// Binding battery reporting      
  
	"zdo bind 0x${device.deviceNetworkId} 21 1 1 {${device.zigbeeId}} {}",
  ]
  return configCmds 
  zigbee.configureReporting( 0x0001, 0x0021, DataType.UINT8, 1,86400, 1 )
	refresh()
}

def refresh() {
  //Straight copy. Need to check device and clusterID for battery.
  def refreshCmds = [
	zigbee.readAttribute(0x0001, 0x0021)
  ]
  //when refresh button is pushed, read updated status
  return refreshCmds
}


private Map parseReadMessage(String description) {
  // Create a map from the message description to make parsing more intuitive
  def msg = zigbee.parseDescriptionAsMap(description)
//  def msg = zigbee.parse(description)
  if (msg.clusterInt==1 && msg.attrInt==32) {
	// call getBatteryResult method to parse battery message into event map
	def result = getBatteryResult(Integer.parseInt(msg.value, 16))
  } else {
	log.debug "Unknown read message received, parsed message: $msg"
  }
  // return map used to create event
  return result
}

private Map parseCatchAllMessage(String description) {
  // Create a map from the raw zigbee message to make parsing more intuitive
  def msg = zigbee.parse(description)
  log.debug msg.clusterId
  switch(msg.clusterId) {
	case 1:
	  // call getBatteryResult method to parse battery message into event map
	  log.debug 'BATTERY MESSAGE'
	  def result = getBatteryResult(Integer.parseInt(msg.value, 16))
	  break
	case 8:
	  switch(msg.command) {
		case 1: // brightness decrease command
		  Map result = [:]
		  result = [
			name: 'Held',
			value: '2',
			data: [buttonNumber: 2],
			descriptionText: "$device.displayName button 2 was held",
			isStateChange: true
		  ]
		  log.debug "Parse returned ${result?.descriptionText}"
		  return result
		  break
		case 3: // brightness change stop command
		  def result = [
			name: 'button',
			value: 'released',
			data: [buttonNumber: [1,2]],
			descriptionText: "$device.displayName button was released",
			isStateChange: true
		  ]
		  log.debug "Recieved stop command"
		  //return result
		  break
		case 5: // brightness increase command
		  Map result = [:]
		  result = [
			name: 'Held',
			value: '1',
			data: [buttonNumber: 1],
			descriptionText: "$device.displayName button 1 was held",
			isStateChange: true
		  ]
		  log.debug "Parse returned ${result?.descriptionText}"
		  return result
		  break
	  }
	case 6:
	  switch(msg.command) {
		case 00: // brightness decrease command
		  Map result = [:]
		  result = [
			name: 'Pushed',
			value: '2',
			data: [buttonNumber: 2],
			descriptionText: "$device.displayName button 2 was pushed",
			isStateChange: true
		  ]
		  log.debug "Parse returned ${result?.descriptionText}"
		  return result
		  break
		case 02: /* brightness change stop command
		  def result = [
			name: 'button',
			value: 'pushed',
			data: [buttonNumber: [1,2]],
			descriptionText: "$device.displayName button was released",
			isStateChange: true
		  ]*/
		  log.debug "What's this?"
		  //return result
		  break
		case 01: // brightness increase command
		  Map result = [:]
		  result = [
			name: 'Pushed',
			value: '1',
			data: [buttonNumber: 1],
			descriptionText: "$device.displayName button 1 was pushed",
			isStateChange: true
		  ]
		  log.debug "Parse returned ${result?.descriptionText}"
		  return result
		  break
	  }
  }
  //New content follows
/*  switch(msg.sourceEndpoint) {
  //Endpoint numbering runs top left, top right, lower left, lower right.
	case 15:
	log.debug "physical button 1"
	Map result = [:]
	result = [
		name: 'button',
		value: 'pushed',
		data: [buttonNumber: 1],
		descriptionText: "$device.displayName button 1 was pushed",
		isStateChange: true
	   ]
	log.debug  "Parse returned ${result?.descriptionText}"
	return result
	break
	case 15: //physical button 3    
	Map result = [:]
	result = [
		name: 'button',
		value: 'pushed',
		data: [buttonNumber: 3],
		descriptionText: "$device.displayName button 3 was pushed",
		isStateChange: true
		]
	log.debug  "Parse returned ${result?.descriptionText}"
	return result
	break
	case 15:
	//physical button 2
	Map result = [:]
	result = [
		name: 'button',
		value: 'pushed',
		data: [buttonNumber: 2],
		descriptionText: "$device.displayName button 2 was pushed",
		isStateChange: true
	   ]
	log.debug  "Parse returned ${result?.descriptionText}"
	return result
	break
	case 15:
	//physical button 4
	Map result = [:]
	result = [
		name: 'button',
		value: 'pushed',
		data: [buttonNumber: 4],
		descriptionText: "$device.displayName button 4 was pushed",
		isStateChange: true
	   ]
	log.debug  "Parse returned ${result?.descriptionText}"
	return result
	break
 //ToDo: Look at how to capture hold down of buttons 3 & 4, it doesn't differentiate on cluster or command as per B1 and B2 .
 } */ 

}

//Motley obtained from other examples, converts battery message into event map.
//AN: I don't think this is working yet.
private Map getBatteryResult(rawValue) {
  def linkText = getLinkText(device)
  def result = [
	name: 'battery',
	value: '--'
  ]
  def volts = rawValue / 10
  def descriptionText
  if (rawValue == 0) {
  } else {
	if (volts > 3.5) {
	  result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
	} else if (volts > 0){
	  def minVolts = 2.1
	  def maxVolts = 3.0
	  def pct = (volts - minVolts) / (maxVolts - minVolts)
	  result.value = Math.min(100, (int) pct * 100)
	  result.descriptionText = "${linkText} battery was ${result.value}%"
	}
  }
  log.debug "Parse returned ${result?.descriptionText}"
  return result
}

I figured this one out. The button was setup wrongly from the factory, which was an easy fix.

Now I have a new question.
The button supports pushed, held and release. But the release is identical on button 1 & 2, and 3 & 4. Can I save a state in the driver on which I can save on held, and use when released?

I seem to be working best when I write to myself :joy:. Solved this one with atomicState.
I will release the code when it's polished.

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.