Scheduler
/**
- Irrigation Controller 8 Zones
- This SmartThings Device Handler (Device Type) Code Works With Arduino Irrigation Controller also available at this site
- Based on v3.1.1 works with SmartThings reduced payload size
- Creates connected irrigation controller
- Author: Stan Dotson (stan@dotson.info) and Matthew Nichols (matt@nichols.name)
- Date: 2014-06-14
- Copyright 2014 Stan Dotson and Matthew Nichols
- 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:
-
Apache License, Version 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 {
input("timer1", "text", title: "Zone One Time", required: false, defaultValue: "1")
input("timer2", "text", title: "Zone Two Time", required: false, defaultValue: "1")
input("timer3", "text", title: "Zone Three Time", required: false, defaultValue: "1")
input("timer4", "text", title: "Zone Four Time", required: false, defaultValue: "1")
input("timer5", "text", title: "Zone Five Time", required: false, defaultValue: "1")
input("timer6", "text", title: "Zone Six Time", required: false, defaultValue: "1")
input("timer7", "text", title: "Zone Seven Time", required: false, defaultValue: "1")
input("timer8", "text", title: "Zone Eight Time", required: false, defaultValue: "1")
input("isDebugEnabled", "bool", title: "Enable debug logging?", defaultValue: false, required: false)
}
metadata {
definition (name: "Irrigation Controller 8 Zones Hubitat v1", version: "1.2", author: "stan@dotson.info", namespace: "d8adrvn/smart_sprinkler") {
capability "Switch"
capability "Momentary"
command "relayOn", [[name:"Relay On", type: "ENUM", constraints: ["1","2","3","4","5","6","7","8"]]]
command "relayOnFor", ["Zone","Duration"]
command "relayOff", [[name:"Relay Off", type: "ENUM", constraints: ["1","2","3","4","5","6","7","8"]]]
command "createZone", ["DEVICE LABEL", "ZONE NUMBER"]
command "update"
command "enablePump"
command "disablePump"
command "onPump"
command "offPump"
attribute "zone1", "string"
attribute "zone2", "string"
attribute "zone3", "string"
attribute "zone4", "string"
attribute "zone5", "string"
attribute "zone6", "string"
attribute "zone7", "string"
attribute "zone8", "string"
attribute "pump", "string"
}
}
// parse events into attributes to create events
def parse(String description) {
def value = parseThingShield(description)
logDebug "Parsing: $value"
if (value == 'null' || value == "" || value?.contains("ping") || value?.trim()?.length() == 0 ) {
// Do nothing
return
}
if (value != "havePump" && value != "noPump" && value != "pumpRemoved") {
String delims = ","
String[] tokens = value?.split(delims)
for (int x=0; x<tokens?.length; x++) {
def displayed = tokens[x] //evaluates whether to display message
def name = tokens[x] in ["on1", "q1", "off1"] ? "zone1"
: tokens[x] in ["on2", "q2", "off2"] ? "zone2"
: tokens[x] in ["on3", "q3", "off3"] ? "zone3"
: tokens[x] in ["on4", "q4", "off4"] ? "zone4"
: tokens[x] in ["on5", "q5", "off5"] ? "zone5"
: tokens[x] in ["on6", "q6", "off6"] ? "zone6"
: tokens[x] in ["on7", "q7", "off7"] ? "zone7"
: tokens[x] in ["on8", "q8", "off8"] ? "zone8"
: tokens[x] in ["onPump", "offPump"] ? "pump"
: tokens[x] in ["ok"] ? "refresh" : null
//manage and display events
def currentVal = device?.currentValue(name)
def newValue = tokens[x].replaceAll("[^A-Za-z]", "" )
def isDisplayed = true
def isPhysical = true
//manage which events are displayed in log
if (newValue == "q") {
isDisplayed = false
isPhysical = false
}
if (newValue == "off" && currentVal == "q") {
isDisplayed = false
isPhysical = false
}
//send an event if there is a state change
if (currentVal != newValue) {
def result = createEvent(name: name, value: newValue, displayed: isDisplayed, isStateChange: true, isPhysical: isPhysical)
logDebug "Parse returned ${result?.descriptionText}"
sendEvent(result)
//Update child devices if on or off
if (newValue in ["on", "off"]) {
def deviceID = "irrigation_" + name
def childDevice = getChildDevice(deviceID)
if (childDevice) {
childDevice.sendEvent(name:"switch", value: newValue)
} else {
log.error "Could not find child device: ${deviceID}"
}
}
}
}
}
if (value == "pumpAdded") {
//send an event if there is a state change
logDebug "parsing pump"
if (device?.currentValue("zone8") != "havePump" && device?.currentValue("pump") != "offPump") {
sendEvent (name:"zone8", value:"havePump", displayed: true, isStateChange: true, isPhysical: true)
sendEvent (name:"pump", value:"offPump", displayed: true, isStateChange: true, isPhysical: true)
}
}
if (value == "pumpRemoved") {
//send event if there is a state change
if (device?.currentValue("pump") != "noPump") {
sendEvent (name:"pump", value:"noPump", displayed: true, isStateChange: true, isPhysical: true)
}
}
if(anyZoneOn()) {
//manages the state of the overall system. Overall state is "on" if any zone is on
//set displayed to false; does not need to be logged in mobile app
if(device?.currentValue("switch") != "on") {
sendEvent (name: "switch", value: "on", descriptionText: "Irrigation System Is On", displayed: false) //displayed default is false to minimize logging
}
} else if(device?.currentValue("switch") != "off") {
sendEvent (name: "switch", value: "off", descriptionText: "Irrigation System Is Off", displayed: false) //displayed default is false to minimize logging
}
}
def anyZoneOn() {
for (int i = 1; i < 9; i++) {
if (device.currentValue("zone${i}") in ["on${i}","q${i}"]) {
break
return true
}
}
return false
}
// handle commands
def relayOff(zoneNumber) {
sendThingShield("off,${zoneNumber}")
}
def relayOn(zoneNumber) {
def zoneTime = settings."timer${zoneNumber}"
sendThingShield("on,${zoneNumber},${zoneTime}")
}
def relayOnFor(zoneNumber, value) {
value = checkTime(value)
sendThingShield("on,${zoneNumber},${value}")
}
def on() {
logDebug "Executing 'allOn'"
for (int i = 1; i < 9; i++) {
sendThingShield("on,${i}," + settings."timer${i}")
}
}
def off() {
sendThingShield("allOff")
}
def checkTime(t) {
def time = (t ?: 0).toInteger()
time > 60 ? 60 : time
}
def update() {
sendThingShield("update")
}
def enablePump() {
sendThingShield("pump,3") //pump is queued and ready to turn on when zone is activated
}
def disablePump() {
sendThingShield("pump,0") //remove pump from system, reactivate Zone8
}
def onPump() {
sendThingShield("pump,2")
}
def offPump() {
sendThingShield("pump,1") //pump returned to queue state to turn on when zone turns on
}
def push() {
sendThingShield("advance") //turn off currently running zone and advance to next
}
def sendThingShield(String message) {
logDebug("sendThingShield: ${message}")
def encodedString = DataType.pack(message, DataType.STRING_CHAR).substring(2)
return "he raw 0x${device.deviceNetworkId} 1 0x${device.endpointId} 0x0 {00000a0a" + encodedString + "}"
}
def parseThingShield(String description) {
if(description?.startsWith('catchall')) {
def data = zigbee.parseDescriptionAsMap(description).data
StringBuilder str = new StringBuilder()
// start at 1, ignore first item, its always 0A
for (int i = 1; i < data.size(); i++) {
str.append((char) Integer.parseInt(data[i], 16))
}
return str.toString()
} else if (description?.startsWith('read attr - ')) {
// parse read attr message manually
def descArr = description.substring("read attr - ".length()).split(',')
def retMap = [:]
descArr.each { listItem ->
def keyValue = listItem.split(':')
retMap.put(keyValue[0].trim(), keyValue[1].trim())
}
String data = retMap['raw'].substring(14)
StringBuilder str = new StringBuilder()
for (int i = 0; i < data.length(); i+=2) {
str.append((char) Integer.parseInt(data.substring(i, i + 2), 16))
}
return str.toString()
} else {
return description
}
}
def createZone(deviceLabel, zoneNumber){
try{
def deviceID = "irrigation_zone" + zoneNumber.toString()
logDebug "Attempting to create Virtual Device: Label: ${deviceLabel}, Zone Number: ${zoneNumber}"
childDevice = addChildDevice("mlritchie", "Irrigation Controller Zone", "${deviceID}", [label: "${deviceLabel}", isComponent: false])
logDebug "createDevice Success"
} catch (Exception e) {
log.warn "Unable to create device."
}
}
private logDebug(msg) {
if (isDebugEnabled) {
log.debug "$msg"
}
}