Reverse Contact State in Aeotec Dry Contact Sensor?

OK, so I probably did a little more than I needed to, but I couldn't stand how non-Hubitat-ty the logging looked, so I added options and cleaned it up a bit (and added info logging they were lacking). I also fixed everything I could that looks like it would have broken if the sensor supports (and was included with) S2 security. I left a lot of oddities that might be normal in ST DTHs (been a while...) but should be harmless on Hubitat.

Most of the other modifications I did were just the usual: the physicalgraph namespace becomes hubitat, the tiles and simulator sections are not used (so can be removed), and whatnot: App and driver porting to Hubitat

If this was too much, I can try again and not try as hard in case that messed something up. I haven't tested this with any real sensors. Hope it works for you!

/**

 *  Copyright 2015 SmartThings

 *

 *  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.

 *

 *  Z-Wave Reversed Contact Sensor.  Does the opposite of the regular door/window sensor.  Built specifically for the

 *  Aeotec Aeon Dry Contact Sensor for @Boatman (John Lee).

 *

 *  Author: SmartThings

 *  Date: 2013-11-3

 *  Version: v2

 *

 *  Updates:

 *  -------

 *  04-17-2016 : Initial commit

 *  12-07-2020 : Modified for Hubitat

 *

 */

metadata {

   definition (name: "My Z-Wave Reversed Contact Sensor", namespace: "jscgs350", author: "jscgs350") {

      capability "ContactSensor"

      capability "Sensor"

      capability "Battery"

      capability "Configuration"

//    fingerprint deviceId: "0x2001", inClusters: "0x30,0x80,0x84,0x85,0x86,0x72"

//    fingerprint deviceId: "0x07", inClusters: "0x30"

//    fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x98", outClusters: "0x5A,0x82"

   }

   preferences {

      input name: "enableDebug", type: "bool", title: "Enable debug logging", defaultValue: true

      input name: "enableDesc", type: "bool", title: "Enable descriptionText logging", defaultValue: true

   }

}

void logsOff(){

   log.warn "Auto-disabling debug logging..."

   device.updateSetting("enableDebug", [value:"false", type:"bool"])

}

def parse(String description) {

   if (enableDebug) log.debug "parse: $description"

   def result = null

   if (description.startsWith("Err 106")) {

      if (getDataValue("S2")) {

         if (enableDebug) log.debug description

      } else {

         log.warn "This sensor failed to complete the network security key exchange. If you are unable to control it, you must remove it from your network and add it again."

         result = createEvent(

            descriptionText: "This sensor failed to complete the network security key exchange. If you are unable to control it, you must remove it from your network and add it again.",

            eventType: "ALERT",

            name: "secureInclusion",

            value: "failed",

            isStateChange: true,

         )

      }

   } else if (description != "updated") {

      def cmd = zwave.parse(description, [0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1])

      if (cmd) {

         result = zwaveEvent(cmd)

      }

   }

   if (enableDebug) log.debug "parsed '$description' to $result"

   return result

}

def updated() {

   if (enableDebug != false) runIn(1800, logsOff)

   def cmds = []

   if (!state.MSR) {

      cmds = [

         zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),

         "delay 1200",

         zwaveSecureEncap(zwave.wakeUpV1.wakeUpNoMoreInformation())

      ]

   } else if (!state.lastbat) {

      cmds = []

   } else {

      cmds = [zwaveSecureEncap(zwave.wakeUpV1.wakeUpNoMoreInformation())]

   }

   response(cmds)

}

def configure() {

   delayBetween([

      zwaveSecureEncap(zwave.manufacturerSpecificV2.manufacturerSpecificGet()),

      batteryGetCommand()

   ], 6000)

}

def sensorValueEvent(value) {

   if (enableDesc && device.currentValue("contact") != (value ? "closed" : "open")) log.info "$device.displayName is ${value ? 'closed' : 'open'}"

   if (value) {

      createEvent(name: "contact", value: "closed", descriptionText: "$device.displayName is closed")

   } else {

      createEvent(name: "contact", value: "open", descriptionText: "$device.displayName is open")

   }

}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicReport cmd)

{

   sensorValueEvent(cmd.value)

}

def zwaveEvent(hubitat.zwave.commands.basicv1.BasicSet cmd)

{

   sensorValueEvent(cmd.value)

}

def zwaveEvent(hubitat.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd)

{

   sensorValueEvent(cmd.value)

}

def zwaveEvent(hubitat.zwave.commands.sensorbinaryv1.SensorBinaryReport cmd)

{

   sensorValueEvent(cmd.sensorValue)

}

def zwaveEvent(hubitat.zwave.commands.sensoralarmv1.SensorAlarmReport cmd)

{

   sensorValueEvent(cmd.sensorState)

}

def zwaveEvent(hubitat.zwave.commands.notificationv3.NotificationReport cmd)

{

   def result = []

   if (cmd.notificationType == 0x06 && cmd.event == 0x16) {

      result << sensorValueEvent(1)

   } else if (cmd.notificationType == 0x06 && cmd.event == 0x17) {

      result << sensorValueEvent(0)

   } else if (cmd.notificationType == 0x07) {

      if (cmd.v1AlarmType == 0x07) {  // special case for nonstandard messages from Monoprice door/window sensors

         result << sensorValueEvent(cmd.v1AlarmLevel)

      } else if (cmd.event == 0x01 || cmd.event == 0x02) {

         result << sensorValueEvent(1)

      } else if (cmd.event == 0x03) {

         if (enableDesc) log.info "$device.displayName covering was removed"

         result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)

         result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))

         if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())

      } else if (cmd.event == 0x05 || cmd.event == 0x06) {

         result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)

      } else if (cmd.event == 0x07) {

         if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())

         result << createEvent(name: "motion", value: "active", descriptionText:"$device.displayName detected motion")

      }

   } else if (cmd.notificationType) {

      def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"

      result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)

   } else {

      def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"

      result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)

   }

   result

}

def zwaveEvent(hubitat.zwave.commands.wakeupv1.WakeUpNotification cmd)

{

   def event = createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)

   def cmds = []

   if (!state.MSR) {

      cmds << zwaveSecureEncap(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))

      cmds << zwaveSecureEncap(zwave.manufacturerSpecificV2.manufacturerSpecificGet())

      cmds << "delay 1200"

   }

   if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {

      cmds << batteryGetCommand()

   } else {

      cmds << zwaveSecureEncap(zwave.wakeUpV1.wakeUpNoMoreInformation())

   }

   [event, response(cmds)]

}

def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {

   def map = [ name: "battery", unit: "%" ]

   if (cmd.batteryLevel == 0xFF) {

      map.value = 1

      map.descriptionText = "${device.displayName} has a low battery"

      map.isStateChange = true

   } else {

      map.value = cmd.batteryLevel

   }

   state.lastbat = now()

   if (enableDesc) log.info "$device.displayName battery is ${map.value}%"

   [createEvent(map), response(zwave.wakeUpV1.wakeUpNoMoreInformation())]

}

def zwaveEvent(hubitat.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {

   def result = []

   def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)

   if (enableDebug) log.debug "msr: $msr"

   updateDataValue("MSR", msr)

   result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)

   if (msr == "011A-0601-0901") {  // Enerwave motion doesn't always get the associationSet that the hub sends on join

      result << response(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))

   } else if (!device.currentState("battery")) {

      if (msr == "0086-0102-0059") {

         result << response(zwave.securityV1.securityMessageEncapsulation().encapsulate(zwave.batteryV1.batteryGet()).format())

      } else {

         result << response(batteryGetCommand())

      }

   }

   result

}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {

   hubitat.zwave.Command encapCmd = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])

   if (encapCmd) {

      zwaveEvent(encapCmd)

   }

}

def zwaveEvent(hubitat.zwave.Command cmd) {

   createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)

}

def batteryGetCommand() {

   zwaveSecureEncap(zwave.batteryV1.batteryGet())

}