Updated device type for Everspring Flood (Utilitech Water Leak) Sensor

dth_sensors
dth_watersensor

(Tony) #1

Hope this might help others … I’ve updated the Everspring Flood Sensor (aka Utilitech Water Leak Sensor). For whatever reason, I was not getting reliable battery reporting, and if/when it did, it was hard to see in the iPhone app. So being new to SmartThings, and being new to Groovy programming, I figured I’d use this as a self-training opportunity. This is one of the biggest reasons I chose SmartThings – opportunities to get my hands dirty and not necessarily have to wait for someone else to always ‘fix’ things.

Anyway, I’ve updated the battery handling, added colored battery levels to the UI for the phone app, and have also added user configurable wake up interval. Device default is 1 hr wake ups. But you can now change it (I was thinking longer intervals for even better battery life). I’ve tested this with both the Everspring Flood and Utilitech Water Leak sensors. Seems to be working very well – but that said, since this is my first go, please let me know if something is greatly mucked up.

Not sure how to best share, so I’ll try pasting here:

/*
 *  Everspring/Utilitech Water Sensor
 *
 *      Everspring Flood Sensor       ST812-2
 *      Utilitech Water Leak Detector TST01-1 (Lowe's)
 *
 *  Author: tosa68
 *  Date:   2014-07-17
 *
 *  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
}

metadata {
	definition (name: "Utilitech Water Sensor", author: "tosa68") {
		capability "Water Sensor"
		capability "Battery"
		capability "Configuration"
        
        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 physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: 0xFF).incomingMessage()
        for (int i = 20; i <= 100; i += 10) {
			status "battery ${i}%": new physicalgraph.zwave.Zwave().batteryV1.batteryReport(batteryLevel: i).incomingMessage()
		}
    }
    
	tiles {
		standardTile("water", "device.water", width: 2, height: 2) {
			state "dry", icon:"st.alarm.water.dry", backgroundColor:"#ffffff"
			state "wet", icon:"st.alarm.water.wet", backgroundColor:"#53a7c0"
		}
		valueTile("battery", "device.battery", inactiveLabel: false, canChangeBackground: true) {
			state "battery", label:'${currentValue}% Battery', unit:"",
            backgroundColors:[
				[value: 19, color: "#BC2323"],
				[value: 20, color: "#D04E00"],
				[value: 30, color: "#D04E00"],
				[value: 40, color: "#DAC400"],
				[value: 41, color: "#79b821"]
			]
		}
		standardTile("configure", "device.configure", inactiveLabel: false, decoration: "flat") {
			state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
		}
		main "water"
		details(["water", "battery", "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}"
    }

	return result
}

def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
    
    // Appears that the Everspring/Utilitech water sensor responds to batteryGet, but not wakeUpNoMoreInformation(?)
    
    def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange:  false)]
    result << response(zwave.batteryV1.batteryGet())

    // 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())
    }
    
    return result
}

def zwaveEvent(physicalgraph.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}"
	}
    
	createEvent(map)
}

def zwaveEvent(physicalgraph.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(physicalgraph.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 = false
        result = [createEvent(map)]
	} else {
		log.debug cmd
	}

    return result
}

def zwaveEvent(physicalgraph.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} reports a low battery"
        map.isStateChange = true
		map.displayed = true
	} else {
		map.value = cmd.batteryLevel
		map.displayed = false
	}

    createEvent(map)
}

def zwaveEvent(physicalgraph.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(physicalgraph.zwave.commands.wakeupv2.WakeUpIntervalReport cmd) {

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

    createEvent(map)
}

def zwaveEvent(physicalgraph.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)

}

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()
}

Water Sensors -- Are Any of Them Reliable?
Device Type preferences: input validation
(Ben Edwards) #2

This is fine. If your code is in github you can share the github llink too.


(Jeff DeWolfe) #3

This is awesome. I like the battery color. Thanks for sharing!


(Beckwith) #4

@tosa

Thanks for the improvements.

My battery shows 90% in red (#BC2323). It looks like it should be green #79b821.

I like the idea of polling battery status less often. If the battery lasts a year, there should be no reason to poll more than once every three days. Right?


(Tony) #5

@beckwith

Well, isn’t that how it goes … no matter how much simulating and testing (even using real devices with real batteries in various states of depletion), once others start using code, strange stuff happens. :slight_smile:

So far the battery colors seem to work as expected in the simulator, and in the iOS mobile app. I don’t have android, so cannot check that. Unfortunately, I’m not yet sure what to make of your 90% in red. But I’ll double check the code, and will watch for any similar reports from others. Wonder if there are bug(s) in backgroundColors handling (that, or I’m not using it correctly)?

Regarding battery poll, yes, that was my thinking too. The device will alarm on wet and low battery, so you won’t miss critical events if increasing the wake up interval. I don’t really need it checking in every hour just to update battery status. Of course, others may feel different, which is why I made it user configurable instead of hardcode. :wink:


(Beckwith) #6

@tosa

I suspect some sort of datatype issue. I’ll try adding more debug.log lines. Here is what I now get:

2ca9d778-8a9b-42ff-aa5d-52d96390da99 8:31:12 AM: debug Parsed BatteryReport(batteryLevel: 80) to [‘name’:‘battery’, ‘unit’:’%’, ‘value’:80, ‘displayed’:false, ‘isStateChange’:false, ‘linkText’:‘Everspring Flood Sensor’, ‘descriptionText’:Everspring Flood Sensor battery is 80%]


(Beckwith) #7

@tosa

Looks like bacgroundColors always uses the first value on Android. Color works on temperature tiles but I can’t seem to see the difference. Anyone else have this issue on their Android?

Regarding the battery poll, I wonder if it drains the battery more during an alarm state.


(Brad B.) #9

Thank you! I have this up and running. The battery, 100%, is green on my Android phone. I set the interval to 12 hours.


(Nathan Cook) #10

I assume to use this I replace the “stock” interface on the app and create a new device type and reconnect device to that, correct? I would think this would be a good update to ST integration as I think everyone has same issue of no battery reporting on these. They are supported yes but don’t work fully (was going to say “work 100%” as that kind of ironic - batteries also report 100%… All the time. Is that bad programming humor?)


(Michael) #11

I’m really sorry to be asking dumb questions, but i just got a hub and previously had an Iris (which I hated and didn’t work well).

I have a Utilitech Water Leak sensor that had been previously paired to the Iris and now I cannot get it to pair with the SmartThings Hub. I have tried removing the batteries for 2+ minutes, trying to reset, etc.

Anyone have any ideas?

Thanks in advance


Utilitech Leak Sensor pairing issue
(Michael Langkilde) #12

Try excluding the device from iris and then use the exclude on st and then try to include it.


(Michael) #13

thanks, actually the issue is that i already got rid of the iris hub

but I was actually able to reset the leak sensor and pair it with the SmartThings Hub.

Seems to be working great now!

thanks again


Utilitech Leak Sensor pairing issue
(Erwenger) #14

Awesome job! Thanks!! I had no battery readings before!


(Kevin) #15

Has anyone tried extending the coverage area of these sensors by adding a long wire to the probe? I’m thinking use a 2 conductor wire and strip the insulation every foot or so. Then lay this long wire on the basement floor around base of my water heater, water softener, sump pit, sewage pump, etc…


#16

Hi @kevin, someone did exactly that with an Ecolink open/close sensor by using the internal screw terminals and some type of wire strip. I just tried searching for the discussion, but I can’t find it. What you propose should work.


#17

Maybe this thread will help


#18

That’s it! Thanks @Jimxenus.


(Scott) #19

Just got an 2 Everspring Flood Sensors. After mounting it I tested to make sure it worked and still could connect to Z-Wave hub. I tested it by licking my fingers then placing the probe on my finger. It alerted and I cancelled it. But it still shows as wet even though it’s clearly not. It’s been 8 hours and still shows wet.


#20

Can you test again and see if that gets it back in sync? How far away from the hub are you. Battery level good?


(Scott) #21

Brand new batteries. Flood sensor is in basement which is pretty far from the hub, but there is another Z-Wave Plus device in room right above it that has no issue connecting to hub. Going on 15 hours and still showing wet. I’ll try testing it again when I get home.