Centralite ZigBee HA Dimmers (3385001-ZHA-W): help needed updating. Will pay for guaranteed function

I have a custom DTH that worked perfect under the old app but now needs updated for the new app. I’m a hobbyist, not a developer so I haven’t been able to figure this out on my own doing research. I’m hoping someone can help fix this existing DTH.

metadata {
definition (name: "Centralite Dimmer", namespace: "", author: "") {
    capability "Button"
    capability "Switch"
    capability "Switch Level"

    fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", manufacturer: "CentraLite Systems", model: "Dimmer Switch", deviceJoinName: "WorksWith Dimmer"
  }

tiles(scale: 2) {
    multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
        tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
            attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
            attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
            attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
            attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
        }
        tileAttribute ("device.level", key: "SLIDER_CONTROL") {
            attributeState "level", action:"switch level.setLevel"
        }
    }
    standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
        state "default", label:"", action:"switch.refresh", icon:"st.secondary.refresh"
    }
    main "switch"
    details(["switch", "refresh"])
}
}

// Parse incoming device messages to generate events
def parse(String description) {
Map map = [:]
log.debug "description is $description"
	
def event = zigbee.getEvent(description)

if (description?.startsWith('catchall:')) {
   // call parseCatchAllMessage to parse the catchall message received
   map = parseCatchAllMessage(description)
   if (map.value == "pushed") {
     List cmds = onoffResponse()
     def result = []
	 log.trace "Sending current state to device ${cmds}"
     result = cmds?.collect { new physicalgraph.device.HubAction(it) }  
     return result
   }
}

if (event) {
	log.info event
	if (event.name == "level") {
		event.value = (event.value as Integer) * 100 / 39  //update level to overcome return of 39 as max from switch
        sendEvent(event)
	}
	else {
   		sendEvent(event)
    }
}
else {
    log.warn "DID NOT PARSE MESSAGE for description : $description"
    log.debug zigbee.parseDescriptionAsMap(description)
}
}

def onoffResponse() {
	log.debug "Creating on/off response"
def swtch = device.currentState("switch")?.value	// Get the current on off value
if (swtch == "on"){
    swtch = "0"								
}else{
    swtch = "1"							
}    
[
   	"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 6 ${swtch} {}"	// Set On or Off
]
}

def off() {
zigbee.off()
}

def on() {
zigbee.on()
}

def setLevel(value) {
zigbee.setLevel(value*39/100)
}

def refresh() {
zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
}

def configure() {
log.debug "Configuring Reporting and Bindings."
zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}

private Map parseCatchAllMessage(String description) {
  // Create a map from the raw zigbee message to make parsing more intuitive
  def msg = zigbee.parse(description)
  Map result = [:]
  switch(msg.clusterId) {
case 6:
  switch(msg.command) {
    case 2: // button pressed
      result = [
        name: 'button',
        value: 'pushed',
        data: [buttonNumber: 1],
        descriptionText: "$device.displayName button was pressed",
        isStateChange: true
      ]
      log.debug "Parse returned ${result?.descriptionText}"
      return result
      break
  }
  }
  result = [value: 'nothing']
}

What problems are you having with it? Details view won’t load in the new app? It may be as simple and removing the device and re-adding it.

Does it still work in the Classic App?

@bban, the switch actuates in the new app but dimming function does not work in the new app. All functionality works fine in classic.

Add a debug log in the setLevel function and see if it’s getting called when you move the slider from the live logging view which is accessible is the IDE. Also print out the value to see if it changes.

How exactly do I add the debug and print the value?

Ad this line right after ‘def setLevel(value) {’ line
log.debug "setLevel called. Value: $value"

Here is what I received in the live log after changing dimmer lever from app 2 times

d739b549-4d84-49d1-af0c-0097851f845e 4:18:48 PM: debug [raw:0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400, profileId:0104, clusterId:0008, sourceEndpoint:01, destinationEndpoint:01, options:0000, messageType:00, dni:7FD6, isClusterSpecific:false, isManufacturerSpecific:false, manufacturerId:0000, command:0B, direction:01, data:[04, 00], clusterInt:8, commandInt:11]

d739b549-4d84-49d1-af0c-0097851f845e 4:18:48 PM: warn DID NOT PARSE MESSAGE for description : catchall: 0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400

d739b549-4d84-49d1-af0c-0097851f845e 4:18:48 PM: debug level updated successfully

d739b549-4d84-49d1-af0c-0097851f845e 4:18:48 PM: debug description is catchall: 0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400

d739b549-4d84-49d1-af0c-0097851f845e 4:18:46 PM: debug setLevel called. Value: 80

d739b549-4d84-49d1-af0c-0097851f845e 4:18:19 PM: debug [raw:0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400, profileId:0104, clusterId:0008, sourceEndpoint:01, destinationEndpoint:01, options:0000, messageType:00, dni:7FD6, isClusterSpecific:false, isManufacturerSpecific:false, manufacturerId:0000, command:0B, direction:01, data:[04, 00], clusterInt:8, commandInt:11]

d739b549-4d84-49d1-af0c-0097851f845e 4:18:19 PM: warn DID NOT PARSE MESSAGE for description : catchall: 0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400

d739b549-4d84-49d1-af0c-0097851f845e 4:18:19 PM: debug level updated successfully

d739b549-4d84-49d1-af0c-0097851f845e 4:18:19 PM: debug description is catchall: 0104 0008 01 01 0000 00 7FD6 00 00 0000 0B 01 0400

d739b549-4d84-49d1-af0c-0097851f845e 4:18:18 PM: debug setLevel called. Value: 53

d739b549-4d84-49d1-af0c-0097851f845e 4:18:18 PM: debug setLevel called. Value: 53

Each time the app sends a pop up error saying “A network or server error occurred. Try again later.”

Is the code you posted what is running? I see the log ‘level updated successfully’ but I don’t see that in the code. Did you add that? Where?

I’m not an expert but it’s very weird it would work on the old app and not the new one, the set level method is being called from the new app so it should work.

The other thing that concerns me is the parse method is not parsing the response from the zigbee device after set Level is executed. This is not setting the level in the device state, but you should still see the device physically dim.

Here is what is running at the time of this posting

metadata {
    definition (name: "Centralite Dimmer", namespace: "sparks244", author: "Reid Keith") {
        capability "Button"
        capability "Switch"
        capability "Switch Level"

        fingerprint profileId: "0104", deviceId: "0101", inClusters: "0000, 0003, 0004, 0005, 0006, 0008", manufacturer: "CentraLite Systems", model: "Dimmer Switch", deviceJoinName: "WorksWith Dimmer"
      }

    tiles(scale: 2) {
        multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
            tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
                attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
                attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
                attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
                attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
            }
            tileAttribute ("device.level", key: "SLIDER_CONTROL") {
                attributeState "level", action:"switch level.setLevel"
            }
        }
        standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", label:"", action:"switch.refresh", icon:"st.secondary.refresh"
        }
        main "switch"
        details(["switch", "refresh"])
    }
}

// Parse incoming device messages to generate events
def parse(String description) {
    Map map = [:]
    log.debug "description is $description"
	
    def event = zigbee.getEvent(description)
    
    if (description?.startsWith('catchall:')) {
       // call parseCatchAllMessage to parse the catchall message received
       map = parseCatchAllMessage(description)
       if (map.value == "pushed") {
         List cmds = onoffResponse()
         def result = []
    	 log.trace "Sending current state to device ${cmds}"
         result = cmds?.collect { new physicalgraph.device.HubAction(it) }  
         return result
       }
    }
    
    if (event) {
    	log.info event
    	if (event.name == "level") {
    		event.value = (event.value as Integer) * 100 / 39  //update level to overcome return of 39 as max from switch
            sendEvent(event)
    	}
    	else {
       		sendEvent(event)
        }
    }
    else {
        log.warn "DID NOT PARSE MESSAGE for description : $description"
        log.debug zigbee.parseDescriptionAsMap(description)
    }
}

def onoffResponse() {
	log.debug "Creating on/off response"
    def swtch = device.currentState("switch")?.value	// Get the current on off value
    if (swtch == "on"){
        swtch = "0"								
    }else{
        swtch = "1"							
    }    
    [
       	"st cmd 0x${device.deviceNetworkId} 0x${device.endpointId} 6 ${swtch} {}"	// Set On or Off
    ]
}

def off() {
    zigbee.off()
}

def on() {
    zigbee.on()
}

def setLevel(value) {
	log.debug "setLevel called. Value: $value"
    zigbee.setLevel(value*39/100)
}

def refresh() {
    zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
}

def configure() {
    log.debug "Configuring Reporting and Bindings."
    zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
}

private Map parseCatchAllMessage(String description) {
  // Create a map from the raw zigbee message to make parsing more intuitive
  def msg = zigbee.parse(description)
  Map result = [:]
  switch(msg.clusterId) {
    case 6:
      switch(msg.command) {
        case 2: // button pressed
          result = [
            name: 'button',
            value: 'pushed',
            data: [buttonNumber: 1],
            descriptionText: "$device.displayName button was pressed",
            isStateChange: true
          ]
          log.debug "Parse returned ${result?.descriptionText}"
          return result
          break
      }
  }
  result = [value: 'nothing']
}

There have been numerous changes in requirements for DTH in the new app. Many won’t work at all. I’m fortunate mine works a little bit.

I do see the bulbs physically dim but the app no longer reflects the current dimming state so I never know where it actually is.

At some point in time we can assume your Zigbee lightbulb changed how it responded or the DTH was updated to not parse it correctly. You can add more debug statements in the parse method where you’re catching all. You might be able to figure out what the level change clusterId is and make a switch case that handles that state change.

However, If that is too daunting, we can cheat (not recommended). If your willing to assume that the device did dim we can update the state within setLevel so hopefully the app will represent the correct state but you may still get network error messages.

def setLevel(value) {
    log.debug "setLevel called. Value: $value"
    zigbee.setLevel(value*39/100)
    state.level = value
    sendEvent(name: "level", value: value)
}