UtiliTech Water Leak Sensor

I switched the driver for the one using generic zwave water to the custom driver and now that device is showing battery every hour in the log too.

I found an updated driver on Tosa68's GitHub.
I've ported it to Hubitat, and it IS working for me just fine.
My battery updates occur along with regular wake-ups in the device's event logs!!!

Posting here if anybody wants to use it.

NOTE 1:
I really do NOT know Groovy. I hear "groovy" and I think 70's!

NOTE 2:
The Toggle Wake Up Status button generates an error in the logs initially. You must let the device go through an update cycle at least once before it will no longer give the error. It doesn't stop the driver from working though, afaik.

/*
 *  Everspring/Utilitech Water Sensor
 *
 *  Everspring Flood Sensor       ST812-2
 *  Utilitech Water Leak Detector TST01-1 (Lowe's)
 *
 *  Author: tosa68
 *  Date:   2014-07-17
 *  ORIGINAL SOURCE: https://raw.githubusercontent.com/tosa68/tosaSmartThings/master/devicetypes/tosa68/utilitech-water-sensor.src/utilitech-water-sensor.groovy
 *
 *  Ported to Hubitat by GatVlieg
 *
 *  Version 0.8 (2016-11-02): changes to (hopefully) catch all WakeUpNotifications
 *  Version 0.9 (2017-03-07): "passive heartbeat"
 *                            - i.e. option to report WakeUp in activity feed;
 *                            - option to always report Battery status in activity feed
 *                              (will report Battery changes regardless of this setting);
 *                            - confirm WakeUpInterval change in activity feed
 *  Version 1.0 (2017-08-14): added capability "Sensor" and "Actuator" for ActionTiles compatiblity (thanks @moritzes)
 *                            changed to multiAttributeTile layout (thanks @johnconstantelo)
 *                            added "Woke Up" tile to toggle display between time of last wake up and elapsed time since last wake up
 *  Version 1.1 (2019-11-18): minor code clean up and update to GitHub                          
 *                             
 *
 *  Features:
 *
 *  1) Battery status updated during configure, at power up, and each wake up
 *  2) User configurable Wake Up Interval
 *
 *  Configure after initial association happens before userWakeUpInterval can be set,
 *  so device will start with default wake up interval of 3600 sec (1 hr).
 *
 *  To set userWakeUpInterval, from the mobile app:
 *    1) enter custom value in device preferences, then
 *         new interval will be set when device next wakes up
 *       OR
 *    3) press 'configure' when the device is awake:
 *         - either just after initial association (within about 10min),
 *         - after power up (remove/reinsert batteries)
 *    
 * 
 *  Copyright 2014 Tony Saye
 *
 *  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.
 *
 */

preferences {
    // manufacturer default wake up is every hour; optionally increase for better battery life
    input "userWakeUpInterval", "number", title: "Wake Up Interval (seconds)", description: "Default 3600 sec (60 sec - 194 days)", defaultValue: '3600', required: false, displayDuringSetup: true
    input "alwaysShowWakeUp", "bool", title: "Report WakeUp", description: "Report when device wakes up in activity feed", required: true, displayDuringSetup: true
    input "alwaysShowBattery", "bool", title: "Always Report Battery", description: "Report battery status in activity feed whether or not it has changed", required: true, displayDuringSetup: true
}

metadata {
	definition (name: "Utilitech Water Sensor", namespace: "tosa68", author: "tony saye") {
		capability "Water Sensor"
		capability "Battery"
		capability "Configuration"
        capability "Sensor"
        capability "Actuator"
        
        command "toggleWakeUpStatus"
        
        fingerprint deviceId: "0xA102", inClusters: "0x86,0x72,0x85,0x84,0x80,0x70,0x9C,0x20,0x71"
	}
/*   
*	simulator {
*		status "dry": "command: 9C02, payload: 00 05 00 00 00"
*		status "wet": "command: 9C02, payload: 00 05 FF 00 00"
*       status "wakeup": "command: 8407, payload: "
*       status "low batt alarm": "command: 7105, payload: 01 FF"
*    	status "battery <20%": new hubitat.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 0xFF).incomingMessage()
*       for (int i = 20; i <= 100; i += 10) {
*			status "battery ${i}%": new hubitat.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i).incomingMessage()
*		}
*    }
*/
/*    
*    tiles (scale:2) {
*		multiAttributeTile(name:"water", type: "generic", width: 6, height: 4) {
*			tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
*				attributeState "dry", label:'${name}', icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
*				attributeState "wet", label:'${name}', icon:"st.alarm.water.wet", backgroundColor:"#00a0dc"
*			}
*		}
*		valueTile("battery", "device.battery", inactiveLabel: false, canChangeBackground: true, width: 2, height: 2) {
*			state "battery", label:'${currentValue}%\nBattery', unit:"",
*           backgroundColors:[
*			[value: 19, color: "#BC2323"],
*				[value: 20, color: "#D04E00"],
*				[value: 30, color: "#D04E00"],
*				[value: 40, color: "#DAC400"],
*				[value: 41, color: "#79b821"]
*			]
*		}
*       valueTile("wakeUpStatus", "device.wakeUpStatus", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
*            state "wakeUpStatus", label:'${currentValue}', unit:"", action: "toggleWakeUpStatus"
*            // green = #44b621; yellow = #f1d801; orange = #d04e00; red = #bc2323
*            // attention orange = #e86d13; battery green = #79b821
*        }
*		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
*			state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
*		}
*		main "water"
*		details(["water", "battery", "wakeUpStatus", "configure"])
*	}
*/    
}

def parse(String description) {
	log.debug "parse: $description"

	def parsedZwEvent = zwave.parse(description, [0x9C: 1, 0x71: 1, 0x84: 2, 0x30: 1, 0x70: 1])
	def result = []

    if (parsedZwEvent) {
        result = zwaveEvent(parsedZwEvent)
        log.debug "Parsed ${parsedZwEvent} to ${result.inspect()}"
    } else {
        log.debug "Non-parsed event: ${description}"
    }

    updateStatus()
    
	return result
}

def zwaveEvent(hubitat.zwave.commands.wakeupv2.WakeUpNotification cmd) {
    
    // Appears that the Everspring/Utilitech water sensor responds to batteryGet, but not wakeUpNoMoreInformation(?)
	/* Oct-2016: troubleshooting not getting wakeUpIntervalSet during WakeUpNotification
	/            Was trying to only send wakeUpInterval if it was changed, but sensor didn't seem to be getting command
	/            during wakeup; now sending wakeUpInterval and getting battery status for each wakeup period
	/            Note: some wakeUp events still seem to be missed, perhaps due to SmartThings latency? But shouldn't be a big
	/                  deal if a wakeUp here or there are missed since we're only setting wakeUpInterval and getting battery
	/                  status.
	*/
    
    //def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange:  false)]
	def map = [:]
    def result
	map.descriptionText = "${device.displayName} woke up"
	map.displayed = true
    if (alwaysShowWakeUp) { map.isStateChange = true } // always report Wake Up events in activity feed
	result = [createEvent(map)]

    // If user has changed userWakeUpInterval, send the new interval to the device 
	/*def userWake = getUserWakeUp(userWakeUpInterval)
    if (state.wakeUpInterval != userWake) {
        state.wakeUpInterval = userWake
        result << response("delay 200")
        result << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId))
        result << response("delay 200")
        result << response(zwave.wakeUpV2.wakeUpIntervalGet())
    }*/

    // Always send wakeUpInterval and get battery status
	state.wakeUpInterval = getUserWakeUp(userWakeUpInterval)
    result << response(zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId))
    result << response("delay 200")
    result << response(zwave.wakeUpV2.wakeUpIntervalGet())
    result << response("delay 200")
    result << response(zwave.batteryV1.batteryGet())
    
    state.lastWakeUp = now()
    
    return result
}

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

	def map = [:]
	if (cmd.sensorType == 0x05) {
		map.name = "water"
		map.value = cmd.sensorState ? "wet" : "dry"
		map.descriptionText = "${device.displayName} is ${map.value}"
	} else {
		map.descriptionText = "${device.displayName}: ${cmd}"
	}
	createEvent(map)
}

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

	def map = [:]
	map.name = "water"
	map.value = cmd.sensorValue ? "wet" : "dry"
	map.descriptionText = "${device.displayName} is ${map.value}"
	createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.alarmv1.AlarmReport cmd) {

	def map = [:]
    def result
	if (cmd.alarmType == 1) {
        if (cmd.alarmLevel == 0xFF) {
		    map.descriptionText = "${device.displayName} has a low battery alarm"
		    map.displayed = true
        } else if (cmd.alarmLevel == 1) {
		    map.descriptionText = "${device.displayName} battery alarm level 1"   // device sometimes returns alarmLevel 1, 
		    map.displayed = false                                                 //   but this appears to be an undocumented alarmLevel(?)
        }
        result = [createEvent(map)]
        result << response(zwave.batteryV1.batteryGet())                          // try to update battery status, but device doesn't seem to always respond
    } else if (cmd.alarmType == 2 && cmd.alarmLevel == 1) {
        map.descriptionText = "${device.displayName} powered up"
		map.displayed = true
        result = [createEvent(map)]
	} else {
		log.debug cmd
	}

    return result
}

def zwaveEvent(hubitat.zwave.commands.batteryv1.BatteryReport cmd) {
	
	def map = [ name: "battery", unit: "%" ]
	if (cmd.batteryLevel == 0xFF) {           // Special value for low battery alert
		map.value = 10                        // will display (and alarm in mobile app) as 10% battery remaining, even though it's really 1%-19% remaining
		map.descriptionText = "${device.displayName} has a low battery"
        map.isStateChange = true
		map.displayed = true
	} else {
		map.value = cmd.batteryLevel
		if (alwaysShowBattery) { map.isStateChange = true } // always report battery level in activity feed
		map.displayed = true
	}
    createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.wakeupv2.WakeUpIntervalCapabilitiesReport cmd) {

    def map = [ name: "defaultWakeUpInterval", unit: "seconds" ]
	map.value = cmd.defaultWakeUpIntervalSeconds
	map.displayed = false

	state.defaultWakeUpInterval = cmd.defaultWakeUpIntervalSeconds
    createEvent(map)
}

def zwaveEvent(hubitat.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) {

	def map = [ name: "reportedWakeUpInterval", unit: "seconds" ]
	map.value = cmd.seconds
	map.displayed = true

    createEvent(map)
}

def zwaveEvent(hubitat.zwave.Command cmd) {
	
    log.debug "COMMAND CLASS: ${cmd}"
    createEvent(descriptionText: "Command not handled: ${cmd}")
}

def configure() {
    
    state.wakeUpInterval = getUserWakeUp(userWakeUpInterval)

    delayBetween([
        zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId).format(),
        zwave.wakeUpV2.wakeUpIntervalSet(seconds:state.wakeUpInterval, nodeid:zwaveHubNodeId).format(),
        zwave.wakeUpV2.wakeUpIntervalGet().format(),
        zwave.batteryV1.batteryGet().format()
    ], 200)

}

def toggleWakeUpStatus() {
    state.toggleWakeUpStatus = state.toggleWakeUpStatus ? false : true
    updateStatus()
}

private updateStatus(){
    def currentInterval = now() - state.lastWakeUp
    def minutesElapsed = (currentInterval/1000/60).toInteger()
    def hoursElapsed = (currentInterval/1000/60/60).toInteger()
    
    //def timeString = new Date().format("MM-dd-yy h:mm a", location.timeZone)
    def today    = new Date(now()).format("MM-dd-yy", location.timeZone)
    def lastDay  = new Date(state.lastWakeUp).format("MM-dd-yy", location.timeZone)
    def lastTime = new Date(state.lastWakeUp).format("h:mm a", location.timeZone)
    
    (lastDay == today) ? (lastTime = "Today\n" + lastTime) : (lastTime = lastDay + "\n" + lastTime)
    
    String statusText = "Woke Up:\n"
    if (state.toggleWakeUpStatus) {
        statusText = statusText + (hoursElapsed ? hoursElapsed.toString() + " hours " : minutesElapsed.toString() + " minutes " ) + "ago"
    } else {
        statusText = statusText + lastTime
    }
    
    if (currentInterval.toInteger()/1000 > state.wakeUpInterval) {
        state.missedWakeUp = (currentInterval.toInteger()/1000/state.wakeUpInterval).toInteger()
        log.debug "current interval: " + currentInterval.toInteger()/1000 + "; wakeUpInterval: " + state.wakeUpInterval
        log.debug "# missed wakeups: " + state.missedWakeUp
    }
    //log.debug statusText
    
    sendEvent(name:"wakeUpStatus", value: statusText, displayed:false)
}

private getUserWakeUp(userWake) {

    if (!userWake)                       { userWake =     '3600' }  // set default 1 hr if no user preference 

    // make sure user setting is within valid range for device 
    if (userWake.toInteger() <       60) { userWake =       '60' }  // 60 sec min
    if (userWake.toInteger() > 16761600) { userWake = '16761600' }  // 194 days max

	/*
     * Ideally, would like to reassign userWakeUpInterval to min or max when needed,
     * so it more obviously reflects in 'preferences' in the IDE and mobile app
     * for the device. Referencing the preference on the RH side is ok, but
     * haven't figured out how to reassign it on the LH side?
     *
     */
    //userWakeUpInterval = userWake 

    return userWake.toInteger()
}
4 Likes

Thank you so much. I tried everything before this. Was about to scrap it. Thank you!

I just bought 3 of the Utilitech water sensors. All 3 pair yet:

  1. Two report wet/dry correctly in Device/Event when tested, but the red led doesn't light up and no beeps.
  2. One does nothing when tested so I assume it's defective.
  3. All 3 use the generic Z-wave water detector driver.

The two that report correctly seem to be working, yet does anyone have a clue why they don't light up or beep?

Thanks!

I just tested two of mine (of 12 installed).
No beep, no light, but it trips.
I think the beep is just for low battery.
Edit: At least you know which one tripped. With the WaterCop Classic, you have to identify that faint beeping.
PS: I still haven't gotten a revised battery report. Maybe I'll try this driver, but I wouldn't want to sacrifice battery life either. They're somewhat of a pain to change.
PPS: I wonder, if it paired, maybe there's a break in the wire.

Thanks for replying, Velvetfoot.
All 3 units paired. None beep or light up when tested, and only one reports wet or changed status.

Here's from the manual:
"Upon leak detection, the Detector will beep, flash a LED light, and report the leak to the Irisβ„’

The two that don't report status change did so intermittently in initial tests, yet now not at all. I did find the instruction sheet online, yet don't see a way to share it here.

I'll try again later. It could have been moisture wasn't sustained long enough.

I just tried two sensors I could get to easily. They beeped, quietly, but no flashing light. I recall they flashed when including.

1 Like

Is there anyway to reverse the signal on this device such that it will let me know when the contact is dry should I happen to want to know when a bucket of water is empty?

Yes, I think so. I just tried that with a simple automation rule.

1 Like

Thank you, I never realized the RM capability till now.

Gosh, I just love my Hubitat so much more each andevery day...dont tell my spouse I say that coz as far as she's concerned that's not in my vocabulary...ha!

1 Like

You're welcome. This is only simple automation, not rule machine.

It's good for me to check these things once in a while. The battery still reads at 100%, and I know that can't be true, lol.

Yah, that batteries in those die in silence, sadly.

I monitor mine with Device Activity Check. They report at least once a day. So I'll know when the batteries die.

I used one of these on ST for 4 years before moving it to Hubitat a year ago; that one still reports the batteries at 80%.

(I'm using the Tosa68's driver as ported by @GatVlieg from post #29)

2 Likes

I just thought I'd put an update here. I've got four of these from long ago. They rarely reported battery and rarely even let the hub know they were present. I could go months with no info from them and I was thinking of replacing them with something more modern.

But then I did the Hub Z-Wave update to version 7.17.1 a few months ago. And now these all report twice daily including battery! The Z-Wave update was totally worth it for me just to keep these four in service.

I like the long cords on them since I can have one mounted conveniently under the kitchen sink and have the sensor end under the dishwasher. Another has the sensor under the washing machine and the unit in the cabinet above the washer where the metal washing and dryer housings don't interfere with the signal.

Chuck

1 Like

Thanks for that update. Did the trick. They seem to stay connected now. I was just about to look for something else to replace them, but the four I have are working good now.

How do you find what version Z-Wave you have? I looked, but couldn't find it. I hit Update on the Z-Wave page, but have no idea where I'm at. Mine were apparently reporting in every few days, but I hadn't been checking. Of course, batteries are at 100% after going on 2 years, lol.

Does the Z-Wave radio get updated with a new hub update?

Thanks much.

The Z-Wave does not get updated with a general hub update. You have to do it separately. There is another thread that describes how to do it. It should be a Firmware Update button on the Z-Wave page:
C7 2.3.1 and Z-Wave Firmware 7.17.1 - :newspaper: News and Updates - Hubitat

This topic was automatically closed 365 days after the last reply. New replies are no longer allowed.