[DEPRECATED] You Got Mail

Nice work. I will add this app to my "to install" list once I get my mailbox situated. Great to see you diving in head first with Groovy.

1 Like

1.0.2 - Added notification option if mailbox was left open

1.0.3 - fixed logic for mailbox left open notification

1.0.4 - added AppWatchDogv2 support

@aaron Nice app. I wanted to know if this app would still work if the mailbox door sensor reads the opposite of the actual door status. For some reason the d/w on the mailbox sends the opposite of what the door actually is, don't know why. It is an ecolink d/w sensor.

How does your app detect the sensor? A change in status, open to close, close to open or does it have to show it is closed first and then open to work?

Also, at the bottom of the setup page where it shows the version, it says vnull.

Thanks for the notice on the null statement. :slight_smile:

Typically I am looking at for a closed sensor to become open and then closed again. Do you have a sensor that stays "open" and then is closed?

@aaron When the door is closed on the mailbox it shows Open on the device page. For some reason the connector block inside the d/w sensor reports the opposite when connected to a magnet switch. So it would report Open (door actually closed), Closed (door opened), Open (door back closed).

So I am looking at the Ecolink d/w sensor. Are you using a magnet or did you extend using the connector block?

I have an Ecolink that I use in conjunction with my wired alarm system. It shows closed when the magnet is close but if I use the relay block internally and place a different end-point (Kiddie relay as an example) it shows open even though the system is "closed." Are you doing the same? If so could you switch back to a magnet?

I am using an external magnet/reed switch on the mailbox door. I think it is a normally open. Wires run to external box that contains the ecolink and connects into internal green connector.

@razorwing

Try this custom device driver for your ECOLink. I swapped the open/closed.

/**
 *  Ecolink Contact Sensor Custom (modified from original Z-Wave Door/Window Sensor)
 * 
 *  importUrl: "https://raw.githubusercontent.com/PrayerfulDrop/Hubitat/master/drivers/ecolink-custom.groovy"
 *
 *  Copyright 2019 Aaron Ward
 *
 *  Original code ported and modified for hubitat from: https://github.com/luder888/SmartThingsPublic/blob/master/devicetypes/luder888/zwave-door-window-sensor-as-smoke-detector.src/zwave-door-window-sensor-as-smoke-detector.groovy
 *
 *  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.
 *
 * ------------------------------------------------------------------------------------------------------------------------------
 *
 *  Changes:
 *  1.0.0 - initial code port and modifications
 */

metadata {
	definition (
            name: "Ecolink Contact Sensor Custom", 
            namespace: "aaronward", 
            author: "Aaron Ward",
            importUrl: "https://raw.githubusercontent.com/PrayerfulDrop/Hubitat/master/drivers/ecolink-custom.groovy"
     ) {
		
        capability "Contact Sensor"
		capability "Battery"
		capability "Configuration"
	}
	preferences() {    	
            input("logEnable", "bool", title: "Enable logging", required: true, defaultValue: false)
    }    

}

def parse(String description) {
	def result = null
	if (description.startsWith("Err 106")) {
		if (state.sec) {
			if(logEnable) log.debug description
		} else {
			result = createEvent(
				descriptionText: "This sensor failed to complete the network security key exchange.",
				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) {
        	if(logEnable) log.debug "valid command"
			result = zwaveEvent(cmd)
		}
	}
	if(logEnable) log.debug "parsed '$description' to $result"
	return result
}

def updated() {
    if (logEnable) {
        log.debug "Logs enabled. Logs will be disabled in 15 minutes."
        runIn(900,logsOff)
    }
	def cmds = []
	if (!state.MSR) {
		cmds = [
			command(zwave.manufacturerSpecificV2.manufacturerSpecificGet()),
			"delay 1200",
			zwave.wakeUpV1.wakeUpNoMoreInformation().format()
		]
	} else if (!state.lastbat) {
		cmds = []
	} else {
		cmds = [zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
	}
	response(cmds)
    
}

def configure() {
	commands([
		zwave.manufacturerSpecificV2.manufacturerSpecificGet(),
		zwave.batteryV1.batteryGet()
	], 6000)
}

def logsOff(){
    log.warn "Debug logging disabled."
    device.updateSetting("logEnable",[value:"false",type:"bool"])
}

def sensorValueEvent(value) {
	if (value) {
		createEvent(name: "contact", value: "closed")
	} else {
		createEvent(name: "contact", value: "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) {
			result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
			if(!state.MSR) result << response(command(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(command(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 << command(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
		cmds << "delay 1200"
	}
	if (!state.lastbat || now() - state.lastbat > 53*60*60*1000) {
		cmds << command(zwave.batteryV1.batteryGet())
	} else {
		cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
	}
	[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()
	[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)
	log.debug "msr: $msr"
	updateDataValue("MSR", msr)

	retypeBasedOnMSR()

	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(command(zwave.batteryV1.batteryGet()))
		}
	}

	result
}

def zwaveEvent(hubitat.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
	def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x25: 1, 0x30: 1, 0x31: 5, 0x80: 1, 0x84: 1, 0x71: 3, 0x9C: 1])
	// log.debug "encapsulated: $encapsulatedCommand"
	if (encapsulatedCommand) {
		state.sec = 1
		zwaveEvent(encapsulatedCommand)
	}
}

def zwaveEvent(hubitat.zwave.Command cmd) {
	createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
}

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

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

def retypeBasedOnMSR() {
	switch (state.MSR) {
		case "0086-0002-002D":
			if(logEnable) log.debug "Changing device type to Z-Wave Water Sensor"
			setDeviceType("Z-Wave Water Sensor")
			break
		case "011F-0001-0001":  // Schlage motion
		case "014A-0001-0001":  // Ecolink motion
		case "014A-0004-0001":  // Ecolink motion +
		case "0060-0001-0002":  // Everspring SP814
		case "0060-0001-0003":  // Everspring HSP02
		case "011A-0601-0901":  // Enerwave ZWN-BPC
			if(logEnable) log.debug "Changing device type to Z-Wave Motion Sensor"
			setDeviceType("Z-Wave Motion Sensor")
			break
		case "013C-0002-000D":  // Philio multi +
			if(logEnable) log.debug "Changing device type to 3-in-1 Multisensor Plus (SG)"
			setDeviceType("3-in-1 Multisensor Plus (SG)")
			break
		case "0109-2001-0106":  // Vision door/window
			if(logEnable) log.debug "Changing device type to Z-Wave Plus Door/Window Sensor"
			setDeviceType("Z-Wave Plus Door/Window Sensor")
			break
		case "0109-2002-0205": // Vision Motion
			if(logEnable) log.debug "Changing device type to Z-Wave Plus Motion/Temp Sensor"
			setDeviceType("Z-Wave Plus Motion/Temp Sensor")
			break
	}
}

@aaron I loaded the custom driver and the states for 'contact' still reads Open. Should it say Closed with the custom driver?
Maybe I will go open and close it to see if there is any change.

EDIT: That fixed it! Thanks Aaron. Much appreciated.

1 Like

1.0.5 - added additional AppWatchDogv2 support

Hi I get this error below, could you help

Can you show me the device information of your contact sensor?

Please see below I made a test with Aqara Sensor, same issue.

Click configure and then open/close your contact sensor.

I did, but error still, please see the log of WADWAZ-1

@emersonvier I see the issue. You didn’t place a mail delivery start/end time. This is the window where Mail is usually delivered to your home.

On next update I will place required fields.

1 Like

A1.0.6 - enforced inputs required for the app to work, added ability to rename app for multiple instances

1 Like

As of 7/25/2020 continued development of this app has ceased. If another developer would like to continue these efforts then please fork the GitHub and also follow the licensing constraints and ownership of the code and intellectual property within the license.