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