Made a Water Leak Sensor

Is anyone getting occasional false alarms on this diy water sensor. Mine’s gotten stuck on wet twice now but pretty spread apart when it happened. I just got my second false alert this morning.

I’ve not done this project, but I have had several false open notices for a monoprice and a few ecolink sensors I’ve got on windows. It’s always the same sensor(s) too, not random. I haven’t had a false notice in a while, but I think the reed sensor is either very sensitive (seems like temperature could be related?), or the magnet is placed just at the fringes of a limit. Even if you’re not using the magnet, the reed sensor could still trip if temperature effects it at all.

I haven’t spent enough time trying to debug this, but now that our temps are changing a lot I’ll spend some time on this when it happens again. What does your device’s event log show?

I’ve had that happen to me once in about a year, I just chalk it up to ST ongoing maintenance issues that I have to contend with. I’m not sure what set it off. I had to pull the battery to reset it.

I will have to check when I get to a PC. I have mine placed at the bottom back end of my aquarium so the only activity there is dust lol. The temps inside the house is indeed changing so that could play a part. I will stick a magnet next to it anf see it unsticks the wet message.

That one day is the only event I have that is logged.

2016-11-13 6:49:04.636 AM MST
1 day ago

I am thinking about building a couple of these. I assume you guys give this the thumbs up? I especially like the ability to run the leads in parallel for two sources (toilet / sink)…

Do it, man. You won’t be sorry.

Yep, if you end up not liking it you can use it as a contact sensor again. I am looking for a more flexible wire/cable to use.

I was thinking about soldering on some pieces of metal to the ends.

My wife is not going to like the look of them with the wire, so I’ll do the contact sensor method for the ones you can’t see and the ST branded ones under the toilets.

A quick cursory look-around the Monoprice web site and I only see the new Z-Wave Plus sensors. I seem to recall someone stating that Monoprice have disabled the ability to utilize the 3rd switch on the new model. In other words, the new Z-Wave Plus sensor cannot be used as a water sensor.

Can anyone confirm this?

I’ve heard that as well. I have two that just shipped. I’ll confirm it for
you if someone else doesn’t beat me to it.

There was some brief discussion in this thread about this topic. I tried it and it didn’t work for me unless I did something wrong. There was something mentioned that this ability had to be enabled in code somehow.

I’ll be trying this method when mine arrive.

Thanks guys! For the first time ever, my house developed a leak under the kitchen sink, which warped several of my gorgeous Brazilian Cherry wood floorboards.

I used my last MonoPrice as a water leak sensor in this area, but since then have decided to outfit the entire house. I truly hope the Z-wave (+) sensors will work.

I have an old GoControl I’m converting to this. I actually took the normal GoControl DHT and changed the contact sensor to a water sensor, but it’s got an issue. Well I do. I don’t know what to change to make the external contacts appear to be a water sensor to ST. I add the capability but I don’t know what or how to tie the zwave commands to that.

I basically want more than what @RBoy and @eradicatore have done. The sensor has three switches, so I wanted to put the magnetic one to use as a validator on my valve. So I mount the sensor next to the valve, put a small magnet on the arm of the valve, when it opens to close the water supply, it triggers the internal contact opening, the water sensor triggers itself with the external contacts. And the tamper switch remains active in case of, I don’t know, mice chewing on it?

Is this feasible? Also, without the water sensor ability properly tied to the external contacts, it doesn’t work as a water sensor to SHM. I have it setup in CoRE as a secondary notification system to SHM anyway, but would be nice to have that native integration. Here’s my code, any thoughts on what to change would be very appreciated!

/**
 *  GoControl Contact Sensor Modified to Leak Sensor v1.6
 *  (WADWAZ-1)
 *
 *  Author: 
 *    Kevin LaFramboise (krlaframboise) Edited Michael Hess
 *
 *  URL to documentation:
 *    https://community.smartthings.com/t/release-gocontrol-door-window-sensor-motion-sensor-and-siren-dth/50728?u=krlaframboise
 *
 *  Changelog:
 *
 *    1.7 (12/14/2016)
 *      - Changed Open to Dry and Closed to Wet
 *
 *    1.6 (06/22/2016)
 *      - Added support for the external contact.
 *      - Added attributes for internal and external contact
 *        so you can use them independently, but the main
 *        contact reflects the last state of either contact.
 *
 *    1.5 (06/19/2016)
 *      -  Bug with initial battery reporting.
 *
 *    1.4.3 (06/17/2016)
 *      -  Fixed issue with battery level being debug logged.
 *
 *    1.4.2 (05/21/2016)
 *      -  Fixing polling so that it doesn't require forcing
 *         state changes or always displaying events.
 *
 *    1.4.1 (05/5/2016)
 *      -  UI Enhancements
 *      -  Added Debug Logging
 *      -  Fixed default tamper state
 *
 *  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.
 *
 */
metadata {
	definition (
		name: "GoControl Contact (Leak) Sensor", 
		namespace: "michaelhess", 
		author: "Michael Hess"
	) {
		capability "Water Sensor"
		capability "Sensor"
		capability "Contact Sensor"
		capability "Battery"
		capability "Tamper Alert"
		capability "Refresh"
		
		attribute "internalContact", "enum", ["open", "close"]
		attribute "externalContact", "enum", ["open", "close"]
		attribute "lastPoll", "number"

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

	// simulator metadata
	simulator {
		status "closed":  "command: 2001, payload: FF"
		status "open": "command: 2001, payload: 00"
	}
	
	preferences {
		input "reportBatteryEvery", "number", 
			title: "Report Battery Every? (Hours)", 
			defaultValue: 4,
			range: "4..167",
			displayDuringSetup: true, 
			required: false
		input "debugOutput", "bool", 
			title: "Enable debug logging?", 
			defaultValue: false, 
			displayDuringSetup: true, 
			required: false
	}
	
	// UI tile definitions
	tiles(scale: 2) {
		multiAttributeTile(name:"contact", type: "generic", width: 6, height: 4, canChangeIcon: true){
			tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
				attributeState "Wet", 
					label:'Wet', 
					icon:"st.contact.contact.wet", 
					backgroundColor:"#ffa81e"
				attributeState "dry", 
					label:'dry', 
					icon:"st.contact.contact.dry", 
					backgroundColor:"#79b821"
			}
		}
		
		valueTile("battery", "device.battery", inactiveLabel: false, 
			decoration: "flat", 
			width: 2, 
			height: 2
		){
			state "battery", 
			label:'${currentValue}% battery', 
			unit:""
		}
		
		valueTile("tamperAlert", "device.tamper", width: 2, height: 2) {
			state "default", label: "", backgroundColor: "#FFFFFF"
			state "clear", label:"Tamper Clear", backgroundColor: "#CCCCCC"
			state "detected", label:"Tamper Detected", backgroundColor: "#FF0000"
		}
		standardTile("refresh", "device.refresh", width: 2, height: 2) {
			state "default", 
				label: "Refresh", 
				action: "refresh", 
				icon:""
		}
		
		main("contact")
		details(["contact", "battery", "tamperAlert", "refresh"])
	}
}

def parse(String description) {		
	def result = []
	if (description.startsWith("Err")) {
		result << createEvent(descriptionText:description, displayed:true)
	} 
	else {		
		def cmd = zwave.parse(description, [0x20: 1, 0x30: 2, 0x80: 1, 0x84: 2, 0x71: 3, 0x86: 1, 0x85: 2, 0x72: 2])		
		if (cmd) {		
			result += zwaveEvent(cmd)
		}
	}
		result << createEvent(name: "lastPoll",value: new Date().time, isStateChange: true, displayed: false)
	return result
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
	def reportEveryHours = settings.reportBatteryEvery ?: 4
	def reportEveryMS = (reportEveryHours * 60 * 60 * 1000)

	def result = []
	if (!state.lastBatteryReport || ((new Date().time) - state.lastBatteryReport > reportEveryMS)) {
		logDebug "Requesting battery level"
		result << response(zwave.batteryV1.batteryGet().format())
		result << response("delay 3000")  
	}
	else {
		logDebug "Skipping battery check because it was already checked within the last $reportEveryHours hours."
	}
	result << response(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
	
	return result
}

def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {	
	def map = [ 
		name: "battery", 		
		unit: "%"
	]
	
	if (cmd.batteryLevel == 0xFF) {
		map.value = 1
		map.descriptionText = "Battery is low"
		map.isStateChange = true
	}
	else {	
		def isNew = (device.currentValue("battery") != cmd.batteryLevel)
		map.value = cmd.batteryLevel
		map.displayed = isNew
		map.isStateChange = isNew
		logDebug "Battery is ${cmd.batteryLevel}%"
	}	
	
	state.lastBatteryReport = new Date().time	
	[
		createEvent(map)
	]
}

def installed() {
	return response([
		zwave.batteryV1.batteryGet().format(),
		zwave.basicV1.basicGet().format()
	])
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
{	
	return createContactEvents(cmd.value, null)
}

def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd)
{	
	
}

def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
	def result = []	

	if (cmd.notificationType == 7) {
		switch (cmd.event) {
			case 3:
				if (cmd.notificationStatus == 0xFF) {
					logDebug "Tamper is detected"
					state.lastBatteryReport = null
					result << createEvent(getTamperEventMap("detected"))		
				}
				break
			case 2:
				result += createContactEvents(cmd.v1AlarmLevel, "internalContact")
				break
			case 0xFE:
				result += createContactEvents(cmd.v1AlarmLevel, "externalContact")
				break
		}
	}	
	return result
}

private createContactEvents(val, contactType) {
	def contactVal = (val == 0xFF) ? "dry" : "wet"
	def desc = "Contact is $contactVal"

	def result = []	
	
	result << createEvent(name: "contact", value: contactVal, isStateChange: true, descriptionText: desc)
	
	if (contactType) {
		logDebug "$desc ($contactType)"
		result << createEvent(name: contactType, value: contactVal, isStateChange: true, descriptionText: desc, displayed: false)
	}
	else {
		logDebug desc
	}
		
	if (device.currentValue("tamper") != "clear") {
		logDebug "Tamper is clear"
		result << createEvent(getTamperEventMap("clear"))
	}
	return result	
}

def zwaveEvent(physicalgraph.zwave.Command cmd) {
	logDebug "Unhandled Command: $cmd"
}

// Resets the tamper attribute to clear.
def refresh() {	
	state.lastBatteryReport = null
	
	if (device.currentValue("tamper") != "clear") {
		sendEvent(getTamperEventMap("clear"))		
	}
	
	logDebug "The Battery level will be refreshed the next time the device wakes up.  If you want this change to happen immediately, open the back cover of the device, wait until the red light turns solid, and then put the cover back on."
	return [zwave.batteryV1.batteryGet().format()]
}

def getTamperEventMap(val) {	
	def isNew = currentTamper() != val
	[
		name: "tamper", 
		value: val, 
		displayed: isNew,
		descriptionText: "Tamper is $val"
	]
}

private currentTamper() {
	return device.currentValue("tamper")
}

def logDebug(msg) {
	if (settings.debugOutput) {
		log.debug "${device.displayName}: $msg"
	}
}

The name of the event you need to raise is “water”.

I think you might just need to change…

result << createEvent(name: "contact", value: contactVal, isStateChange: true, descriptionText: desc)

to

result << createEvent(name: "water", value: contactVal, isStateChange: true, descriptionText: desc)

1 Like

I updated my DHT and will test at lunch, man I hope it’s that easy! Thank you!

Are you wanting to use it for both a contact and a water sensor? Contact for the magnet and water for the terminals?

Correct.

10 char