Caring is Sharing WallMote & ZW97

Special Thanks to @thebearmay
He took one the DH that I had for the WallMote and sorted out some of the issues with this DH. Now it works perfectly.

/**
 *  Aeon WallMote (with slide functionality)
 *
 *  Copyright 2019 Custom
 *
 *  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.
 *
 *  Aeon WallMote Quad
 *  https://aeotec.freshdesk.com/support/solutions/articles/6000162392-wallmote-quad-user-guide-
 *  https://aeotec.freshdesk.com/support/solutions/articles/6000166184-wallmote-quad-technical-specifications-
 *  https://aeotec.freshdesk.com/support/solutions/articles/6000187144-wallmote-quad-group-association-explanation-for-direct-communication-
 *
 *  Aeon WallMote Dual
 *  https://aeotec.freshdesk.com/support/solutions/articles/6000176572-wallmote-user-guide-
 *  https://aeotec.freshdesk.com/support/solutions/articles/6000176570-wallmote-technical-specification-
 *  No group associations document but the above document for quad is applicable
 *
 *  Author: custom (custom)
 *  Original Author: Eric Maycock (erocm123)
 *  Date: 2019-01-23
 *
 *  Change Log:
 *  2020-01-23: Initial
 *  2020-01-26: Fixed logging for those that switch to this driver and have never set a logging level yet
 *              Change to two pushed buttons instead of one button that is pushed and held
 *              Bug fixes for some events
 *              Handled the event sent when a user initiates a connectivity health test from the remote's button
 *              Fixed firmware reporting for current Hubitat command classes
 *
 *
 *  Previous Author's Change Log:
 *  2017-06-19: Added check to only send color change config for three wakeups. Editing preferences
 *              and hitting "done" will reset the counter. This is an attempt to prevent freezing
 *              caused by updating preferences.
 */
 
metadata {
  definition (name: "Aeon WallMote", namespace: "Custom", author: "Custom",
      importUrl: "https://raw.githubusercontent.com/codahq/hubitat_codahq/master/devicestypes/aeon-wallmote.groovy") {
    capability "Actuator"
    capability "PushableButton"
    capability "HoldableButton"
    capability "ReleasableButton"
    capability "Configuration"
    capability "Sensor"
    capability "Battery"
    capability "Health Check"

    attribute "sequenceNumber", "number"
    attribute "needUpdate", "string"

    fingerprint mfr: "0086", prod: "0102", model: "0082", deviceJoinName: "Aeon WallMote"

    fingerprint deviceId: "0x1801", inClusters: "0x5E,0x73,0x98,0x86,0x85,0x59,0x8E,0x60,0x72,0x5A,0x84,0x5B,0x71,0x70,0x80,0x7A", outClusters: "0x25,0x26" // secure inclusion
    fingerprint deviceId: "0x1801", inClusters: "0x5E,0x85,0x59,0x8E,0x60,0x86,0x70,0x72,0x5A,0x73,0x84,0x80,0x5B,0x71,0x7A", outClusters: "0x25,0x26"

  }
  preferences {
    input description: "Once you change values on this page, the attribute value \"needUpdate\" will show \"YES\" until all configuration parameters are updated.", title: "<b>Settings</b>", displayDuringSetup: false, type: "paragraph", element: "paragraph"
    generate_preferences(configuration_model())
  }

}

def parse(String description) {
  logDebug "parse(String description)"
  logTrace "description: $description"
  
  def results = []
  if (description.startsWith("Err")) {
    results = createEvent(descriptionText:description, displayed:true)
  }
  else {
    def cmd = zwave.parse(description, [0x2B: 1, 0x80: 1, 0x84: 1])
    if(cmd) results += zwaveEvent(cmd)
    if(!results) results = [ descriptionText: cmd, displayed: false ]
  }

  return results
}

def zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd)"
  logTrace "cmd: $cmd"
  //not needed. do nothing.
}

def zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd)"
  logTrace "cmd: $cmd"
  //not needed. do nothing.
}

def zwaveEvent(hubitat.zwave.commands.switchmultilevelv3.SwitchMultilevelStartLevelChange cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.switchmultilevelv3.SwitchMultilevelStartLevelChange cmd)"
  logTrace "cmd: $cmd"
 
  def result = []
  
  //if not held buttons and button slide is enabled
  if (!getHeldButtons() && settings."3" == "1") {
    switch (cmd.upDown) {
      case false: // Up
        logTrace "Slide up"
        result << buttonEvent(device.currentValue("numberOfButtons") - 1, "pushed")
        break
      case true: // Down
        logTrace "Slide down"
        result << buttonEvent(device.currentValue("numberOfButtons"), "pushed")
        break
      default:
        logDebug "Unhandled SwitchMultilevelStartLevelChange: ${cmd}"
        break
    }
  }
  result
}

def zwaveEvent(hubitat.zwave.commands.centralscenev1.CentralSceneNotification cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.centralscenev1.CentralSceneNotification cmd)"
  logTrace "cmd: $cmd"

  def result = []
  
  sendEvent(name: "sequenceNumber", value: cmd.sequenceNumber, displayed:false)
  switch (cmd.keyAttributes) {
    case 0:
      result << buttonEvent(cmd.sceneNumber, "pushed")
      break
    case 1: // released
      state."${cmd.sceneNumber}" = cmd.keyAttributes
      if (!settings.holdMode || settings.holdMode == "2") result << buttonEvent(cmd.sceneNumber, "held")
      result << buttonEvent(cmd.sceneNumber, "released")
      break
    case 2: // held
      state."${cmd.sceneNumber}" = cmd.keyAttributes
      if (settings.holdMode == "1") result << buttonEvent(cmd.sceneNumber, "held")
      break
    default:
      logDebug "Unhandled CentralSceneNotification: ${cmd}"
      break
  }
  result
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
  def encapsulatedCommand = cmd.encapsulatedCommand([0x5B: 1, 0x20: 1, 0x31: 5, 0x30: 2, 0x84: 1, 0x70: 1])
  state.sec = 1
  if (encapsulatedCommand) {
    zwaveEvent(encapsulatedCommand)
  }
  else {
    log.warn "Unable to extract encapsulated cmd from $cmd"
    createEvent(descriptionText: cmd.toString())
  }
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
  response(configure())
}

def zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpIntervalReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpIntervalReport cmd)"
  logTrace "cmd: $cmd"
  
  state.wakeInterval = cmd.seconds
}

def zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpNotification cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpNotification cmd)"
  logTrace "cmd: $cmd"
  
  logInfo "Device ${device.displayName} woke up"

  def request = update_needed_settings()

  request << zwave.versionV1.versionGet()

  if (!state.lastBatteryReport || (now() - state.lastBatteryReport) / 60000 >= 60 * 24) {
    logDebug "Over 24hr since last battery report. Requesting report"
    request << zwave.batteryV1.batteryGet()
  }

  state.wakeCount? (state.wakeCount = state.wakeCount + 1) : (state.wakeCount = 2)

  if (request != []) {
    response(commands(request) + ["delay 5000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()])
  }
  else {
    logDebug "No commands to send"
    response([zwave.wakeUpV1.wakeUpNoMoreInformation().format()])
  }
}

def buttonEvent(button, name) {
  def msg = "$device.displayName button $button was $name"
  logInfo msg
  createEvent(name: name, value: button, descriptionText: msg, isStateChange: true)
}

def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd)"
  logTrace "cmd: $cmd"
  
  def map = [ name: "battery", unit: "%" ]
  if (cmd.batteryLevel == 0xFF) {
    map.value = 1
    map.descriptionText = "${device.displayName} battery is low"
    map.isStateChange = true
  }
  else {
    map.value = cmd.batteryLevel
  }
  state.lastBatteryReport = now()
  createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd)"
  logTrace "cmd: $cmd"
  state."association${cmd.groupingIdentifier}" = cmd.nodeId[0]
}

def zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd) {
  update_current_properties(cmd)
  logDebug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd2Integer(cmd.configurationValue)}'"
}

def zwaveEvent(hubitat.zwave.commands.configurationv1.ConfigurationReport cmd) {
  update_current_properties(cmd)
  logDebug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd2Integer(cmd.configurationValue)}'"
}

def zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd) {
  def fw = "${cmd.firmware0Version}.${cmd.firmware0SubVersion}"
  updateDataValue("fw", fw)
  if (state.MSR == "003B-6341-5044") {
    updateDataValue("ver", "${cmd.firmware0Version >> 4}.${cmd.firmware0SubVersion & 0xF}")
  }
  def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
  createEvent(descriptionText: text, isStateChange: false)
}

def zwaveEvent(hubitat.zwave.commands.powerlevelv1.PowerlevelTestNodeReport cmd) {
  logDebug "zwaveEvent(hubitat.zwave.commands.powerlevelv1.PowerlevelTestNodeReport cmd)"
  logTrace "cmd: $cmd"
  logInfo "Connectivity testing performed by user at remote..."
}

def zwaveEvent(hubitat.zwave.Command cmd) {
  log.warn "Unhandled zwaveEvent: ${cmd}"
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
  def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
  logDebug "msr: $msr"
  updateDataValue("MSR", msr)
}

def installed() {
  log.info "...Aeon WallMote Installed..."
  configure()
}

/**
* Triggered when Done button is pushed on Preference Pane
*/
def updated() {
  logDebug "updated() is being called"
  state.wakeCount = 1
  def cmds = update_needed_settings()
  sendEvent(name: "checkInterval", value: 2 * 60 * 12 * 60 + 5 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID])
  sendEvent(name: "numberOfButtons", value: getNumButtons(), displayed: true)
  sendEvent(name:"needUpdate", value: device.currentValue("needUpdate"), displayed:false, isStateChange: true)
  if (cmds != []) response(commands(cmds))
}

def configure() {
  logDebug "Configuring Device For Use"
  def cmds = []
  cmds = update_needed_settings()
  sendEvent(name: "numberOfButtons", value: getNumButtons(), displayed: true)
  if (cmds != []) commands(cmds)
}

private getNumButtons() {
  return settings.buttons ? (settings."3" == "1" ? settings.buttons.toInteger() + 2 : settings.buttons) : (settings."3" ? 4 + 2 : 4)
}

def ping() {
  logDebug "ping()"
  log.warn "Battery Device - Not sending ping commands"
}

def generate_preferences(configuration_model) {
  def configuration = new XmlSlurper().parseText(configuration_model)

  configuration.Value.each {
    switch(it.@type) {   
      case ["byte","short","four"]:
        input "${it.@index}", "number", title:"<b>${it.@label}</b>", description: "${it.Help}", range: "${it.@min}..${it.@max}", defaultValue: "${it.@value}",
          displayDuringSetup: "${it.@displayDuringSetup}", required: "${it.@required}"
        break
      case "list":
        def items = []
        it.Item.each { items << ["${it.@value}": "${it.@label}"] }
        input "${it.@index}", "enum", title:"<b>${it.@label}</b>", description: "${it.Help}", defaultValue: "${it.@value}", options: items,
          displayDuringSetup: "${it.@displayDuringSetup}", required: "${it.@required}"
        break
      case "decimal":
        input "${it.@index}", "decimal", title:"<b>${it.@label}</b>", description: "${it.Help}", range: "${it.@min}..${it.@max}", defaultValue: "${it.@value}",
          displayDuringSetup: "${it.@displayDuringSetup}", required: "${it.@required}"
        break
      case "boolean":
        input "${it.@index}", "bool", title: "<b>${it.@label}</b>", description: "${it.Help}", defaultValue: "${it.@value}",
          displayDuringSetup: "${it.@displayDuringSetup}", required: "${it.@required}"
        break
      case "string":
        input "${it.@index}", "text", title: "<b>${it.@label}</b>", description: "${it.Help}", defaultValue: "${it.@value}",
          displayDuringSetup: "${it.@displayDuringSetup}", required: "${it.@required}"
        break
    }  
  }
}

def update_current_properties(cmd) {
  def currentProperties = state.currentProperties ?: [:]

  currentProperties."${cmd.parameterNumber}" = cmd.configurationValue

  if (settings."${cmd.parameterNumber}" != null) {
    if (convertParam(cmd.parameterNumber, settings."${cmd.parameterNumber}") == cmd2Integer(cmd.configurationValue)) {
      sendEvent(name:"needUpdate", value:"NO", displayed:false, isStateChange: true)
    }
    else {
      sendEvent(name:"needUpdate", value:"YES", displayed:false, isStateChange: true)
    }
  }

  state.currentProperties = currentProperties
}

def update_needed_settings() {
  def cmds = []
  def currentProperties = state.currentProperties ?: [:]

  def configuration = new XmlSlurper().parseText(configuration_model())
  def isUpdateNeeded = "NO"

  if (state.wakeInterval == null || state.wakeInterval != 86400) {
    logDebug "Setting Wake Interval to 86400"
    cmds << zwave.wakeUpV1.wakeUpIntervalSet(seconds: 86400, nodeid:zwaveHubNodeId)
    cmds << zwave.wakeUpV1.wakeUpIntervalGet()
  }

  if (settings."3" == "1") {
    if (!state.association3 || state.association3 == "" || state.association3 == "1") {
      logDebug "Setting association group 3"
      cmds << zwave.associationV2.associationSet(groupingIdentifier:3, nodeId:zwaveHubNodeId)
      cmds << zwave.associationV2.associationGet(groupingIdentifier:3)
    }
    if (!state.association5 || state.association5 == "" || state.association5 == "1") {
      logDebug "Setting association group 5"
      cmds << zwave.associationV2.associationSet(groupingIdentifier:5, nodeId:zwaveHubNodeId)
      cmds << zwave.associationV2.associationGet(groupingIdentifier:5)
    }
    if (!state.association7 || state.association7 == "" || state.association7 == "1") {
      logDebug "Setting association group 7"
      cmds << zwave.associationV2.associationSet(groupingIdentifier:7, nodeId:zwaveHubNodeId)
      cmds << zwave.associationV2.associationGet(groupingIdentifier:7)
    }
    if (!state.association9 || state.association9 == "" || state.association9 == "1") {
      logDebug "Setting association group 9"
      cmds << zwave.associationV2.associationSet(groupingIdentifier:9, nodeId:zwaveHubNodeId)
      cmds << zwave.associationV2.associationGet(groupingIdentifier:9)
    }
  }

  if (state.MSR == null){
    logDebug "Getting Manufacturer Specific Info"
    cmds << zwave.manufacturerSpecificV2.manufacturerSpecificGet()
  }

  configuration.Value.each {     
    if ("${it.@setting_type}" == "zwave") {
      if (currentProperties."${it.@index}" == null) {
        if (it.@setonly == "true"){
          if (it.@index == 5) {
            if (state.wakeCount <= 3) {
              logDebug "Parameter ${it.@index} will be updated to " + convertParam(it.@index.toInteger(), settings."${it.@index}"? settings."${it.@index}" : "${it.@value}")
              def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}"? settings."${it.@index}" : "${it.@value}")
              cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
              cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
            }
            else {
              logDebug "Parameter has already sent. Will not send again until updated() gets called"
            }
          }
          else {
            logDebug "Parameter ${it.@index} will be updated to " + convertParam(it.@index.toInteger(), settings."${it.@index}"? settings."${it.@index}" : "${it.@value}")
            def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}"? settings."${it.@index}" : "${it.@value}")
            cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
            cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
          }
        }
        else {
          isUpdateNeeded = "YES"
          logDebug "Current value of parameter ${it.@index} is unknown"
          cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
        }
      }
      else if (settings."${it.@index}" != null && cmd2Integer(currentProperties."${it.@index}") != convertParam(it.@index.toInteger(), settings."${it.@index}")) { 
        isUpdateNeeded = "YES"

        if (it.@index == 5) {
          if (state.wakeCount <= 3) {
            logDebug "Parameter ${it.@index} will be updated to " + convertParam(it.@index.toInteger(), settings."${it.@index}")
            def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}")
            cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
            cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
          }
          else {
            logDebug "Parameter has already sent. Will not send again until updated() gets called"
          }
        }
        else {
          logDebug "Parameter ${it.@index} will be updated to " + convertParam(it.@index.toInteger(), settings."${it.@index}")
          def convertedConfigurationValue = convertParam(it.@index.toInteger(), settings."${it.@index}")
          cmds << zwave.configurationV1.configurationSet(configurationValue: integer2Cmd(convertedConfigurationValue, it.@byteSize.toInteger()), parameterNumber: it.@index.toInteger(), size: it.@byteSize.toInteger())
          cmds << zwave.configurationV1.configurationGet(parameterNumber: it.@index.toInteger())
        }
      } 
    }
  }

  sendEvent(name:"needUpdate", value: isUpdateNeeded, displayed:false, isStateChange: true)
  return cmds
}

def convertParam(number, value) {
  long parValue
  switch (number) {
    case 5:
      switch (value) {
        case "1": 
          parValue = 4278190080
          break
        case "2": 
          parValue = 16711680
          break
        case "3": 
          parValue = 65280
          break
        default:
          parValue = value
          break
      }
      break
    default:
      parValue = value.toLong()
      break
  }
  return parValue
}

private def getHeldButtons() {
  def heldButtons = (1..device.currentValue("numberOfButtons"))
  heldButtons = heldButtons.find { button ->
    state."${button}" == 2
  }
  logTrace "heldButtons $heldButtons"
  return heldButtons
}

private logInfo(msg) {
  if (settings.loggingLevel?.toInteger() >= 1) log.info msg
}

def logDebug(msg) {
  if (settings.loggingLevel?.toInteger() >= 2) log.debug msg
}

def logTrace(msg) {
  if (settings.loggingLevel?.toInteger() >= 3) log.trace msg
}

/**
* Convert 1 and 2 bytes values to integer
*/
def cmd2Integer(array) { 
  long value
  if (array != [255, 0, 0, 0]){
    switch(array.size()) {    
      case 1:
      value = array[0]
      break
      case 2:
      value = ((array[0] & 0xFF) << 8) | (array[1] & 0xFF)
      break
      case 3:
      value = ((array[0] & 0xFF) << 16) | ((array[1] & 0xFF) << 8) | (array[2] & 0xFF)
      break
      case 4:
      value = ((array[0] & 0xFF) << 24) | ((array[1] & 0xFF) << 16) | ((array[2] & 0xFF) << 8) | (array[3] & 0xFF)
      break
    }
  }
  else {
    value = 4278190080
  }
  return value
}

def integer2Cmd(value, size) {
  switch(size) {
    case 1:
      [value.toInteger()]
      break
    case 2:
      def short value1   = value & 0xFF
      def short value2 = (value >> 8) & 0xFF
      [value2.toInteger(), value1.toInteger()]
      break
    case 3:
      def short value1   = value & 0xFF
      def short value2 = (value >> 8) & 0xFF
      def short value3 = (value >> 16) & 0xFF
      [value3.toInteger(), value2.toInteger(), value1.toInteger()]
      break
    case 4:
      def short value1 = value & 0xFF
      def short value2 = (value >> 8) & 0xFF
      def short value3 = (value >> 16) & 0xFF
      def short value4 = (value >> 24) & 0xFF
      [value4.toInteger(), value3.toInteger(), value2.toInteger(), value1.toInteger()]
      break
  }
}

private command(hubitat.zwave.Command cmd) {
  if (state.sec) {
    zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
  }
  else {
    cmd.format()
  }
}

private commands(commands, delay=1000) {
  delayBetween(commands.collect{ command(it) }, delay)
}

def configuration_model() {
'''
<configuration>
  <Value type="list" byteSize="1" index="buttons" label="WallMote Model" min="2" max="4" value="" setting_type="preference" deviceId="" displayDuringSetup="true">
    <Help>
Which model of WallMote is this?
    </Help>
    <Item label="Dual" value="2" />
    <Item label="Quad" value="4" />
  </Value>
  <Value type="list" byteSize="1" index="1" label="Touch Sound" min="0" max="1" value="1" setting_type="zwave" deviceId="">
    <Help>
Enable/disable the touch sound.<br/>
Default: Enable
    </Help>
    <Item label="Disable" value="0" />
    <Item label="Enable" value="1" />
  </Value>
  <Value type="list" byteSize="1" index="2" label="Touch Vibration" min="0" max="1" value="1" setting_type="zwave" deviceId="">
    <Help>
Enable/disable the touch vibration.<br/>
Default: Enable
    </Help>
    <Item label="Disable" value="0" />
    <Item label="Enable" value="1" />
  </Value>
  <Value type="list" byteSize="1" index="3" label="Button Slide" min="0" max="1" value="1" setting_type="zwave" deviceId="">
    <Help>
Enable/disable the function of button slide.<br/>
Default: Enable
    </Help>
    <Item label="Disable" value="0" />
    <Item label="Enable" value="1" />
  </Value>
  <Value type="list" byteSize="4" index="5" label="LED Color" min="1" max="3" value="3" setting_type="zwave" deviceId="" setonly="true">
    <Help>
To configure which color will be displayed when the button is pressed.<br/>
Default: Blue
    </Help>
    <Item label="Red" value="1" />
    <Item label="Green" value="2" />
    <Item label="Blue" value="3" />
  </Value>
  <Value type="list" byteSize="4" index="holdMode" label="Hold Mode" min="1" max="2" value="2" setting_type="preference" deviceId=""> 
    <Help>
Multiple "held" events on botton hold? With this option, the controller will send a "held" event about every second while holding down a button. If set to No it will send a "held" event a single time when the button is released.<br/>
Default: No
    </Help>
    <Item label="No" value="2" />
    <Item label="Yes" value="1" />
  </Value>
  <Value type="list" byteSize="1" index="loggingLevel" label="Logging Level?" min="0" max="3" value="0" setting_type="preference" deviceId="" required="true">
    <Help>
Set the verbosity of the logs.
    </Help>
    <Item label="No Logging" value="0" />
    <Item label="Descriptive Text" value="1" />
    <Item label="Debug" value="2" />
    <Item label="Trace" value="3" />
  </Value>
</configuration>
'''
}
def push(btnNum) {
    sendEvent(name:"pushed", value: btnNum.intValue(), descriptionText: "Button ${btnNum.intValue()} was pushed (digital)", isStateChange: true)
}

def release(btnNum) {
    sendEvent(name:"released", value: btnNum.intValue(), descriptionText: "Button ${btnNum.intValue()} was released (digital)", isStateChange: true)
}

def hold(btnNum) {
    sendEvent(name:"held", value: btnNum.intValue(), descriptionText: "Button ${btnNum.intValue()} was held (digital)", isStateChange: true)
}

I had bought the EVALogik ZW97 and I had some issues with the existing DH that defaults with Hubitat. I worked with the manufacturer, and they created a DH that works perfectly too. I would like to share with you all.

EVALogik ZW97

/**
 *  Outdoor Smart Plug 2-Channel ZW97
 *  Author: 
 *  Date: 
 *   Reference:https://raw.githubusercontent.com/InovelliUSA/Hubitat/master/Drivers/inovelli-2-channel-outdoor-smart-plug-nzw97.src/inovelli-2-channel-outdoor-smart-plug-nzw97.groovy
 *  Copyright 2020
 *
 *  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.
 *
 *  2019-11-20: Fixed Association Group management.
 *
 *  2018-05-02: Added support for Z-Wave Association Tool SmartApp. Associations require firmware 1.02+.
 *
 */
import java.util.concurrent.*
metadata {
    definition(
        name: "Outdoor Smart Plug 2-Channel ZW97", 
        namespace: "Evalogik",
        author: "sky-nie",
        importUrl: "https://raw.githubusercontent.com/sky-nie/hubitat/main/evalogik/evalogik-outdoor-smart-plug.groovy"
    ) {
        capability "Actuator"
        capability "Sensor"
        capability "Switch"
        capability "Polling"
        capability "Refresh"
        capability "Health Check"
        capability "PushableButton"
        capability "Configuration"

        attribute "lastActivity", "String"
        attribute "lastEvent", "String"

        command "setAssociationGroup", [[name: "Group Number*",type:"NUMBER", description: "Provide the association group number to edit"],
                                        [name: "Z-Wave Node*", type:"STRING", description: "Enter the node number (in hex) associated with the node"],
                                        [name: "Action*", type:"ENUM", constraints: ["Add", "Remove"]],
                                        [name:"Multi-channel Endpoint", type:"NUMBER", description: "Currently not implemented"]]

        command "childOn"
        command "childOff"
        command "childRefresh"
        command "componentOn"
        command "componentOff"
        command "componentRefresh"

        fingerprint mfr: "0312", prod: "C000", deviceId: "C007", deviceJoinName: "Outdoor Smart Plug 2-Channel ZW97", inClusters:"0x5E,0x6C,0x55,0x9F"  // ZW97
        fingerprint mfr: "0312", prod: "C000", deviceId: "C007", deviceJoinName: "Outdoor Smart Plug 2-Channel ZW97", inClusters:"0x86,0x25,0x85,0x8E,0x59,0x60,0x72,0x5A,0x73,0x70,0x7A"
    }

    simulator {}

    preferences {
        input "autoOff1", "number", title: "Auto Off Channel 1\n\nAutomatically turn switch off after this number of seconds\nRange: 0 to 32767", description: "Tap to set", required: false, range: "0..32767"
        input "autoOff2", "number", title: "Auto Off Channel 2\n\nAutomatically turn switch off after this number of seconds\nRange: 0 to 32767", description: "Tap to set", required: false, range: "0..32767"
        input "ledIndicator", "enum", title: "LED Indicator\n\nTurn LED indicator on when switch is:\n", description: "Tap to set", required: false, options:[["0": "On"], ["1": "Off"], ["2": "Disable"]], defaultValue: "0"
        input description: "Use the \"Z-Wave Association Tool\" SmartApp to set device associations. (Firmware 1.02+)\n\nGroup 2: Sends on/off commands to associated devices when switch is pressed (BASIC_SET).", title: "Associations", displayDuringSetup: false, type: "paragraph", element: "paragraph"
    }

    tiles {
        multiAttributeTile(name: "switch", type: "lighting", width: 6, height: 4, canChangeIcon: true) {
            tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
                attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
                attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff"
                attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
                attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#00a0dc", nextState: "turningOff"
            }
        }

        standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
            state "default", label: "", action: "refresh.refresh", icon: "st.secondary.refresh"
        }

        valueTile("lastActivity", "device.lastActivity", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
            state "default", label: 'Last Activity: ${currentValue}',icon: "st.Health & Wellness.health9"
        }

        valueTile("icon", "device.icon", inactiveLabel: false, decoration: "flat", width: 4, height: 1) {
            state "default", label: '', icon: "https://inovelli.com/wp-content/uploads/Device-Handler/Inovelli-Device-Handler-Logo.png"
        }
    }
}

private static getCommandClassVersions() {
    [
            0x20: 1, // Basic
            0x25: 1, // Switch Binary
            0x70: 2, // Configuration
            0x60: 3, // Multi Channel
            0x8E: 2, // Multi Channel Association
            0x72: 2, // Manufacturer Specific
            0x85: 2, // Association
            0x86: 1, // Version
    ]
}

com.hubitat.app.DeviceWrapper getTargetDeviceByEndPoint(ep = null ) {
    if (ep) {
        return getChildDevices().find{ (it.deviceNetworkId.split("-ep")[-1] as Integer) == (ep as Integer)}
    } else {
        return device
    }
}

String secure(String cmd, ep = null ){
    if (ep) {
        return zwaveSecureEncap(zwave.multiChannelV4.multiChannelCmdEncap(sourceEndPoint: 0, bitAddress: 0, res01:0, destinationEndPoint: ep).encapsulate(cmd))
    } else {
        return zwaveSecureEncap(cmd)
    }
}

String secure(hubitat.zwave.Command cmd, ep = null ){
    if (ep) {
        return zwaveSecureEncap(zwave.multiChannelV4.multiChannelCmdEncap(sourceEndPoint: 0, bitAddress: 0, res01:0, destinationEndPoint: ep).encapsulate(cmd))
    } else {
        return zwaveSecureEncap(cmd)
    }
}

def parse(String description) {
    def result = []
    def cmd = zwave.parse(description, commandClassVersions)
    if (cmd) {
        result += zwaveEvent(cmd)
        log.debug "Parsed ${cmd} to ${result.inspect()}"
    } else {
        log.debug "Non-parsed event: ${description}"
    }

    def now
    if(location.timeZone)
        now = new Date().format("yyyy MMM dd EEE h:mm:ss a", location.timeZone)
    else
        now = new Date().format("yyyy MMM dd EEE h:mm:ss a")
    sendEvent(name: "lastActivity", value: now, displayed:false)

    return result
}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd, ep = null) {
    log.debug "BasicReport ${cmd} - ep ${ep}"
    if (ep) {
        def event
        childDevices.each {
            childDevice ->
                if (childDevice.deviceNetworkId == "$device.deviceNetworkId-ep$ep") {
                    childDevice.sendEvent(name: "switch", value: cmd.value == 0xFF ? "on" : "off")
                }
        }
        if (cmd.value == 0xFF) {
            event = [createEvent([name: "switch", value: "on"])]
        } else {
            def allOff = true
            childDevices.each {
                n ->
                    if (n.currentState("switch").value != "off") allOff = false
            }
            if (allOff) {
                event = [createEvent([name: "switch", value: "off"])]
            } else {
                event = [createEvent([name: "switch", value: "on"])]
            }
        }
        return event
    }
}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd, ep = null) {
    log.debug "SwitchBinaryReport ${cmd} - ep ${ep}"
    if (ep) {
        def event
        def childDevice = childDevices.find {
            it.deviceNetworkId == "$device.deviceNetworkId-ep$ep"
        }
        if (childDevice) {
            childDevice.sendEvent(name: "switch", value: cmd.value == 0xFF ? "on" : "off")
        }
        sendEvent(name: "ep" + ep, value: cmd.value == 0xFF ? "on" : "off", displayed: true)
        if (cmd.value == 0xFF) {
            event = [createEvent([name: "switch", value: "on"])]
        } else {
            def allOff = true
            childDevices.each {
                n->
                    if (n.deviceNetworkId != "$device.deviceNetworkId-ep$ep" && n.currentState("switch")?.value != "off") allOff = false
            }
            if (allOff) {
                event = [createEvent([name: "switch", value: "off"])]
            } else {
                event = [createEvent([name: "switch", value: "on"])]
            }
        }
        sendEvent(event)
        return []
    } else {
        def result = createEvent(name: "switch", value: cmd.value == 0xFF? "on" : "off", type: "digital")
        sendEvent(result)
        return [] // returns the result of reponse()
    }
}

def zwaveEvent(hubitat.zwave.commands.multichannelv3.MultiChannelCmdEncap cmd) {
    // log.debug "MultiChannelCmdEncap ${cmd}"
    def encapsulatedCommand = cmd.encapsulatedCommand(commandClassVersions)
    if (encapsulatedCommand) {
        zwaveEvent(encapsulatedCommand, cmd.sourceEndPoint as Integer)
    }
}

def zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionReport cmd, ep = null )
{
    log.debug "SupervisionReport ${cmd} - ep ${ep}"
    com.hubitat.app.DeviceWrapper targetDevice = getTargetDeviceByEndPoint(ep)

    ConcurrentHashMap whatThisDeviceSent = supervisionSentCommands?.get(device.getDeviceNetworkId() as String)

    switch (cmd.status as Integer)
    {
        case 0x00: // "No Support"
            whatWasSent = whatThisDeviceSent?.remove(cmd.sessionID as Integer)
            if (ignoreSupervisionNoSupportCode()) {
                if (logEnable) log.warn "Received a 'No Support' supervision report ${cmd} for command ${whatWasSent}, but this device has known problems with its Supervision implementation so the 'No Support' code was ignored."
            } else 	{
                log.warn "Device ${targetDevice.displayName}: Z-Wave Command supervision reported as 'No Support' for command ${whatWasSent}. If you see this warning repeatedly, please report as an issue on https://github.com/jvmahon/HubitatCustom/issues. Please provide the manufacturer, deviceType, and deviceId code for your device as shown on the device's Hubitat device web page."
            }
            break
        case 0x01: // "working"
            whatWasSent = whatThisDeviceSent?.get(cmd.sessionID as Integer)
            if (txtEnable) log.info "Device ${targetDevice.displayName}: Still processing command: ${whatWasSent}."
            runIn(5, supervisionCheck)
            break
        case 0x02: // "Fail"
            whatWasSent = whatThisDeviceSent?.remove(cmd.sessionID as Integer)
            log.warn "Device ${targetDevice.displayName}: Z-Wave supervised command reported failure. Failed command: ${whatWasSent}."
            sendUnsupervised(zwave.basicV1.basicGet(), ep)
            break
        case 0xFF: // "Success"
            whatWasSent = whatThisDeviceSent?.remove(cmd.sessionID as Integer)
            if (txtEnable || logEnable) log.info "Device ${targetDevice.displayName}: Device successfully processed supervised command ${whatWasSent}."
            break
    }
    if (whatThisDeviceSent?.size() < 1) unschedule(supervisionCheck)
}

def zwaveEvent(hubitat.zwave.commands.supervisionv1.SupervisionGet cmd, ep = null ) {
    //log.debug "SupervisionGet ${cmd} - ep ${ep}"
    hubitat.zwave.Command encapsulatedCommand = cmd.encapsulatedCommand(defaultParseMap)

    if (encapsulatedCommand) {
        if ( ep ) {
            zwaveEvent(encapsulatedCommand, ep)
        } else {
            zwaveEvent(encapsulatedCommand)
        }
    }

    hubitat.zwave.Command confirmationReport = (new hubitat.zwave.commands.supervisionv1.SupervisionReport(sessionID: cmd.sessionID, reserved: 0, moreStatusUpdates: false, status: 0xFF, duration: 0))
    sendHubCommand(new hubitat.device.HubAction(secure(confirmationReport, ep), hubitat.device.Protocol.ZWAVE))
}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
    log.debug "ManufacturerSpecificReport ${cmd}"
    def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
    log.debug "msr: $msr"
    updateDataValue("MSR", msr)
}

def zwaveEvent(hubitat.zwave.Command cmd) {
    // This will capture any commands not handled by other instances of zwaveEvent
    // and is recommended for development so you can see every command the device sends
    log.debug "Unhandled Event: ${cmd}"
}

def on() {
    log.debug "on()"
    commands([
            //zwave.switchAllV1.switchAllOn(),
            encap(zwave.basicV1.basicSet(value: 0xFF), 1),
            encap(zwave.basicV1.basicSet(value: 0xFF), 2)
    ])
}

def off() {
    log.debug "off()"
    commands([
            //zwave.switchAllV1.switchAllOff(),
            encap(zwave.basicV1.basicSet(value: 0x00), 1),
            encap(zwave.basicV1.basicSet(value: 0x00), 2)
    ])
}

def childOn(String dni) {
    log.debug "childOn($dni)"
    def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0xFF), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds
}

def childOff(String dni) {
    log.debug "childOff($dni)"
    def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.basicV1.basicSet(value: 0x00), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    //cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds
}

def childRefresh(String dni) {
    log.debug "childRefresh($dni)"
    def cmds = []
    cmds << new hubitat.device.HubAction(command(encap(zwave.switchBinaryV1.switchBinaryGet(), channelNumber(dni))), hubitat.device.Protocol.ZWAVE)
    cmds
}

def componentOn(cd) {
    if (infoEnable) log.info "${device.label?device.label:device.name}: componentOn($cd)"
    return childOn(cd.deviceNetworkId)
}

def componentOff(cd) {
    if (infoEnable) log.info "${device.label?device.label:device.name}: componentOff($cd)"
    return childOff(cd.deviceNetworkId)
}

def componentRefresh(cd) {
    if (infoEnable) log.info "${device.label?device.label:device.name}: componentRefresh($cd)"
    return childRefresh(cd.deviceNetworkId)
}

def poll() {
    log.debug "poll()"
    refresh()
}

def refresh() {
    log.debug "refresh()"
    commands([
            encap(zwave.switchBinaryV1.switchBinaryGet(), 1),
            encap(zwave.switchBinaryV1.switchBinaryGet(), 2),
    ])
}

def ping() {
    log.debug "ping()"
    refresh()
}

def installed() {
    refresh()
}

def configure() {
    log.debug "configure()"
    def cmds = initialize()
    commands(cmds)
}

def integer2Cmd(value, size) {
    try{
        switch(size) {
            case 1:
                [value]
                break
            case 2:
                short value1   = value & 0xFF
                short value2 = (value >> 8) & 0xFF
                [value2, value1]
                break
            case 3:
                short value1   = value & 0xFF
                short value2 = (value >> 8) & 0xFF
                short value3 = (value >> 16) & 0xFF
                [value3, value2, value1]
                break
            case 4:
                short value1 = value & 0xFF
                short value2 = (value >> 8) & 0xFF
                short value3 = (value >> 16) & 0xFF
                short value4 = (value >> 24) & 0xFF
                [value4, value3, value2, value1]
                break
        }
    } catch (e) {
        log.debug "Error: integer2Cmd $e Value: $value"
    }
}

def updated() {
    if (!state.lastRan || now() >= state.lastRan + 2000) {
        log.debug "updated()"
        state.lastRan = now()
        def cmds = initialize()
        commands(cmds)
    } else {
        log.debug "updated() ran within the last 2 seconds. Skipping execution."
    }
}

def initialize() {
    log.debug "initialize()"
    if (!childDevices) {
        createChildDevices()
    } else if (device.label != state.oldLabel) {
        childDevices.each {
            if (it.label == "${state.oldLabel} (CH${channelNumber(it.deviceNetworkId)})") {
                def newLabel = "${device.displayName} (CH${channelNumber(it.deviceNetworkId)})"
                it.setLabel(newLabel)
            }
        }
        state.oldLabel = device.label
    }
    sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 2 * 60, displayed: false, data: [protocol: "zwave", hubHardwareId: device.hub.hardwareID, offlinePingable: "1"])
    sendEvent(name: "numberOfButtons", value: 1, displayed: true)
    def cmds = processAssociations()
    cmds << zwave.configurationV1.configurationSet(scaledConfigurationValue: ledIndicator!=null? ledIndicator.toInteger() : 0, parameterNumber: 1, size: 1)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 1)
    cmds << zwave.configurationV1.configurationSet(configurationValue: autoOff1!=null? integer2Cmd(autoOff1.toInteger(), 2) : integer2Cmd(0,2), parameterNumber: 2, size: 2)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 2)
    cmds << zwave.configurationV1.configurationSet(configurationValue: autoOff2!=null? integer2Cmd(autoOff2.toInteger(), 2) : integer2Cmd(0,2), parameterNumber: 3, size: 2)
    cmds << zwave.configurationV1.configurationGet(parameterNumber: 3)
    return cmds
}

def zwaveEvent(hubitat.zwave.commands.configurationv2.ConfigurationReport cmd) {
    log.debug "${device.displayName} parameter '${cmd.parameterNumber}' with a byte size of '${cmd.size}' is set to '${cmd.configurationValue}'"
}

private encap(cmd, endpoint) {
    if (endpoint) {
        zwave.multiChannelV3.multiChannelCmdEncap(destinationEndPoint: endpoint).encapsulate(cmd)
    } else {
        cmd
    }
}

private command(hubitat.zwave.Command cmd) {
    if (state.sec) {
        zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
    } else {
        cmd.format()
    }
}

private commands(commands, delay = 500) {
    delayBetween(commands.collect {
        command(it)
    }, delay)
}

private static channelNumber(String dni) {
    dni.split("-ep")[-1] as Integer
}
private void createChildDevices() {
    state.oldLabel = device.label
    for (i in 1..2) {
        addChildDevice("hubitat", "Generic Component Switch", "${device.deviceNetworkId}-ep${i}", [completedSetup: true, label: "${device.displayName} (CH${i})",
            isComponent: false, componentName: "ep$i", componentLabel: "Channel $i"
        ])
    }
}

def setDefaultAssociations() {
    def smartThingsHubID = String.format('%02x', zwaveHubNodeId).toUpperCase()
    state.defaultG1 = [smartThingsHubID]
    state.defaultG2 = []
    state.defaultG3 = []
}

def setAssociationGroup(group, nodes, action, endpoint = null){
    // Normalize the arguments to be backwards compatible with the old method
    action = "${action}" == "1" ? "Add" : "${action}" == "0" ? "Remove" : "${action}" // convert 1/0 to Add/Remove
    group  = "${group}" =~ /\d+/ ? (group as int) : group                             // convert group to int (if possible)
    nodes  = [] + nodes ?: [nodes]                                                    // convert to collection if not already a collection

    if (! nodes.every { it =~ /[0-9A-F]+/ }) {
        log.error "invalid Nodes ${nodes}"
        return
    }

    if (group < 1 || group > maxAssociationGroup()) {
        log.error "Association group is invalid 1 <= ${group} <= ${maxAssociationGroup()}"
        return
    }

    def associations = state."desiredAssociation${group}"?:[]
    nodes.each {
        node = "${it}"
        switch (action) {
            case "Remove":
                if (logEnable) log.debug "Removing node ${node} from association group ${group}"
                associations = associations - node
                break
            case "Add":
                if (logEnable) log.debug "Adding node ${node} to association group ${group}"
                associations << node
                break
        }
    }
    state."desiredAssociation${group}" = associations.unique()
}

def maxAssociationGroup(){
    if (!state.associationGroups) {
        if (logEnable) log.debug "Getting supported association groups from device"
        zwave.associationV2.associationGroupingsGet() // execute the update immediately
    }
    (state.associationGroups?: 5) as int
}

def processAssociations(){
    def cmds = []
    setDefaultAssociations()
    def associationGroups = maxAssociationGroup()
    for (int i = 1; i <= associationGroups; i++){
        if(state."actualAssociation${i}" != null){
            if(state."desiredAssociation${i}" != null || state."defaultG${i}") {
                def refreshGroup = false
                ((state."desiredAssociation${i}"? state."desiredAssociation${i}" : [] + state."defaultG${i}") - state."actualAssociation${i}").each {
                    if (logEnable) log.debug "Adding node $it to group $i"
                    cmds << zwave.associationV2.associationSet(groupingIdentifier:i, nodeId:hubitat.helper.HexUtils.hexStringToInt(it))
                    refreshGroup = true
                }
                ((state."actualAssociation${i}" - state."defaultG${i}") - state."desiredAssociation${i}").each {
                    if (logEnable) log.debug "Removing node $it from group $i"
                    cmds << zwave.associationV2.associationRemove(groupingIdentifier:i, nodeId:hubitat.helper.HexUtils.hexStringToInt(it))
                    refreshGroup = true
                }
                if (refreshGroup) cmds << zwave.associationV2.associationGet(groupingIdentifier:i)
                else if (logEnable) log.debug "There are no association actions to complete for group $i"
            }
        } else {
            if (logEnable) log.debug "Association info not known for group $i. Requesting info from device."
            cmds << zwave.associationV2.associationGet(groupingIdentifier:i)
        }
    }
    return cmds
}

void zwaveEvent(hubitat.zwave.commands.associationv2.AssociationReport cmd) {
    def temp = []
    if (cmd.nodeId != []) {
        cmd.nodeId.each {
            temp += it.toString().format( '%02x', it.toInteger() ).toUpperCase()
        }
    }
    state."actualAssociation${cmd.groupingIdentifier}" = temp
    log.debug "Associations for Group ${cmd.groupingIdentifier}: ${temp}"
    updateDataValue("associationGroup${cmd.groupingIdentifier}", "$temp")
}

def zwaveEvent(hubitat.zwave.commands.associationv2.AssociationGroupingsReport cmd) {
    log.debug "Supported association groups: ${cmd.supportedGroupings}"
    state.associationGroups = cmd.supportedGroupings
    createEvent(name: "groups", value: cmd.supportedGroupings)
}

void zwaveEvent(hubitat.zwave.commands.versionv1.VersionReport cmd) {
    log.debug cmd
    if(cmd.applicationVersion && cmd.applicationSubVersion) {
        def firmware = "${cmd.applicationVersion}.${cmd.applicationSubVersion.toString().padLeft(2,'0')}"
        state.needfwUpdate = "false"
        sendEvent(name: "status", value: "fw: ${firmware}")
        updateDataValue("firmware", firmware)
    }
}

I Hope you find these helpful!!

7 Likes

I'm gradually moving all of my devices from my old Samsung hub to a new Hubitat C8 Pro and this was my first need for a custom driver. Thanks for the post.

Can you share what the problems are with the built-in driver? I saw an allusion to that above but no specifics. If there is something that needs to be fixed to make it work out-of-box, that can be done. But in any case, I'm happy someone in the community has a driver you like!

2 Likes

I'm not sure which driver you're referring to. The Wallmote Built-In driver s would not do any of the advanced functions. Such as slide to open or close or any of those other features. If you're referring to the outdoor Z-Wave outlet. It wasn't working properly at all. You could turn on and off the outlet and not all the outlets would turn on. The vendor corrected that problem and it's been working ever since.

1 Like