Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
284 changes: 213 additions & 71 deletions devicetypes/gussery3/orbit-timer.src/orbit-timer.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* 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
* 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
Expand All @@ -29,6 +29,8 @@ metadata {
capability "Switch"
capability "Valve"

command "checkdev"

fingerprint profileId: "0104", inClusters: "0000,0001,0003,0020,0006,0201", outClusters: "000A,0019"
}

Expand All @@ -43,109 +45,237 @@ metadata {
reply "zcl on-off off": "on/off: 0"
}

preferences {
input "debugOutput", "bool",
title: "Enable debug logging?",
defaultValue: true,
displayDuringSetup: false,
required: false
}

// UI tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: 'closed', action: "switch.on", icon: "st.Outdoor.outdoor16", backgroundColor: "#ffffff"
state "on", label: 'open', action: "switch.off", icon: "st.Outdoor.outdoor16", backgroundColor: "#53a7c0"
state "off", label: 'closed', action: "on", icon: "st.Outdoor.outdoor16", backgroundColor: "#ffffff"
state "on", label: 'open', action: "off", icon: "st.Outdoor.outdoor16", backgroundColor: "#53a7c0"
}
valueTile("battery", "device.battery", decoration: "flat") {
valueTile("battery", "device.battery", decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("refresh", "device.refresh", decoration: "flat") {
state "refresh", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
standardTile("refresh", "device.refresh", decoration: "flat") {
state "refresh", label:'', action:"checkdev", icon:"st.secondary.refresh"
}
standardTile("reInit", "device.refresh", decoration: "flat") {
state "refresh", label:'reInit', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("open", "device.switch", decoration: "flat", inactiveLabel: false) {
state "on", label:'open', action:"open", icon:"st.Outdoor.outdoor16", backgroundColor: "#53a7c0"
}
standardTile("close", "device.switch", decoration: "flat", inactiveLabel: false) {
state "off", label:'close', action:"close", icon:"st.Outdoor.outdoor16", backgroundColor: "#ffffff"
}
main "switch"
details(["switch","battery","refresh"])
details(["switch","battery","refresh","reInit","open", "close"])
}
}

// Public methods
def installed() {
log.trace "installed()"
}

def uninstalled() {
log.trace "uninstalled()"
}

def configure() {
log.trace "configure"
/*
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}"
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)
return zigbee.readAttribute(0x0006, 0x0000) +
zigbee.readAttribute(0x0001, 0x0020) +
zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) +
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)
*/
return zigbee.onOffConfig() +
zigbee.configureReporting(0x0001, 0x0020, 0x20, 600, 21600, 0x01) + // Configure Battery reporting
zigbee.onOffRefresh() +
zigbee.readAttribute(0x0001, 0x0020) // get a Battery Report
}

def checkdev() {
if(!state.refreshLastRanAt || now() >= state.refreshLastRanAt + 4000) {
state.refreshLastRanAt = now()
log.trace "sending check device status"
} else {
log.debug "refresh(): Ran within last 4 seconds - SKIPPING"
return
}
return zigbee.onOffRefresh() +
zigbee.readAttribute(0x0001, 0x0020) // get a Battery Report
}

def refresh() {
refresh1(true)
}

def refresh1(logMsg) {
if(!state.refreshLastRanAt || now() >= state.refreshLastRanAt + 4000) {
state.refreshLastRanAt = now()
if(logMsg) { log.trace "sending refresh command" }
} else {
log.debug "refresh(): Ran within last 4 seconds - SKIPPING"
return
}
/*
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}"
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)
"st rattr 0x${device.deviceNetworkId} 1 6 0"
zigbee.readAttribute(0x0001, 0x0020) // battery voltage
return zigbee.readAttribute(0x0006, 0x0000) +
zigbee.readAttribute(0x0001, 0x0020) +
zigbee.configureReporting(0x0006, 0x0000, 0x10, 0, 600, null) +
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)
*/
return zigbee.onOffRefresh() +
zigbee.readAttribute(0x0001, 0x0020) + // get a Battery Report
zigbee.onOffConfig() +
zigbee.configureReporting(0x0001, 0x0020, 0x20, 600, 21600, 0x01) // Configure Battery reporting
}


def initialize() {
if(!state.updatedLastRanAt || now() >= state.updatedLastRanAt + 4000) {
log.info "initialize..."
state.updatedLastRanAt = now()
state.isInstalled = true
unschedule()
fireCommand(refresh())
} else {
log.trace "initialize(): Ran within last 4 seconds - SKIPPING"
return
}
}

def updated() {
initialize()
log.info "updated..."
}

// Parse incoming device messages to generate events
def parse(String description) {
log.info description
def result1 = zigbee.getEvent(description)

if(result1) {
sendEvent(result1)
} else
{
if (description?.startsWith("catchall:")) {
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
def msg = zigbee.parse(description)
log.debug "Parse returned ${result?.descriptionText}"
return result
log.trace msg
log.trace "data: $msg.data"
//logDebug("parse: $description")
state.parseLastRanAt = now()
def result = zigbee.getEvent(description)

def res = []
if(result) {
/*
This should be able to parse switch message
*/
log.trace "parse_result: $result"
if(result?.name == "switch") {
if(result?.value == "off") {
res << createEvent(name: "valve", value: "closed")
} else {
res << createEvent(name: "valve", value: "open")
}
}
res << createEvent(result)
return res
} else {
if(description?.startsWith('read attr -')) {
return parseReportAttributeMessage(description)
} else {
Map descMap = zigbee.parseDescriptionAsMap(description)
if (descMap.clusterId == "8021") { // Bind_rsp
log.trace "Received Bind rsp"
} else if( (descMap.clusterId == "0001" || descMap.clusterId == "0006") && descMap.commandInt == 7) {
if(descMap.data[0] == "00") {
log.trace "Received read attribute response SUCCESS ${descMap.clusterId}"
} else {
log.debug "attribute ERROR ${descMap}"
}
} else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug ${descMap}
def msg = zigbee.parse(description)
log.trace "parse msg: $msg"
}
}
}
else if (description?.startsWith('read attr -')) {
return parseReportAttributeMessage(description)
}
else {
def name = description?.startsWith("on/off: ") ? "switch" : null
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
def result = createEvent(name: name, value: value)
log.debug "Parse returned ${result?.descriptionText}"
return result
}
}

def myPoll() {
def cmd = ""
def howLong = now() - state?.parseLastRanAt
if( howLong > (12 * 60 * 1000)) {
sendEvent(name: "switch", value:"off")
sendEvent(name: "valve", value:"closed")
cmd = refresh1(false)
log.debug "myPoll refresh ${howLong}ms"
} else {
log.trace "myPoll()"
cmd = zigbee.readAttribute(0x0006, 0x0000) +
zigbee.readAttribute(0x0001, 0x0020)
}
fireCommand(cmd)
}

// Commands to device
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
'zcl on-off on'
log.trace "on()"
/*
This device only turns on for 10 mins at a time
*/
runIn((10*60+40), "myPoll", [overwrite: true])
sendEvent(name: "switch", value:"on")
sendEvent(name: "valve", value:"open")
return zigbee.on() // +
//zigbee.readAttribute(0x0006, 0x0000)
}

def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
'zcl on-off off'
log.trace "off()"
sendEvent(name: "switch", value:"off")
sendEvent(name: "valve", value:"closed")
unschedule("myPoll")
return zigbee.off() //+
//zigbee.readAttribute(0x0006, 0x0000)
}

def open() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
log.trace "open()"
return on()
}

def close() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
}

def refresh() {
log.debug "sending refresh command"
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}"
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)
"st rattr 0x${device.deviceNetworkId} 1 6 0"
zigbee.readAttribute(0x0001, 0x0020)

}

def configure() {

"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}"
zigbee.configureReporting(0x0001, 0x0020, 0x20, 30, 21600, 0x01)

log.trace "close()"
return off()
}

private parseReportAttributeMessage(String description) {
/*
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
log.debug "Desc Map: $descMap"
*/

Map descMap = zigbee.parseDescriptionAsMap(description)
def results = []
if (descMap.cluster == "0001" && descMap.attrId == "0020") {
log.debug "Received battery level report"
//if (descMap.cluster == "0001" && descMap.attrId == "0020") {
if (descMap.clusterInt == 1 && descMap.attrInt == 0x20) { // Cluster Power, Voltage
results = createEvent(getBatteryResult(Integer.parseInt(descMap.value, 16)))
log.trace "Received battery level report ${results}"
} else {
if (descMap.clusterInt == 0 && descMap.attrInt == 5) { // ModelIdentifier
log.trace "Received model identifier"
} else {
log.debug "UNKNOWN Desc Map: $descMap"
}
}

return results
Expand All @@ -157,21 +287,33 @@ private getBatteryResult(rawValue) {
def result = [name: 'battery']

def volts = rawValue / 10
def descriptionText
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {
} else {
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "${linkText} battery was ${result.value}%"
}

return result
}
// Private methods
private fireCommand(List commands) { //Function used from SmartThings Lightify Dimmer Switch support by Adam Outler
if (commands != null && commands.size() > 0) {
log.trace("Executing commands:" + commands)
for (String value : commands) {
sendHubCommand([value].collect {new physicalgraph.device.HubAction(it)})
}
}
}

private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex()
}
}

private logDebug(msg) {
if (settings?.debugOutput != false) {
log.debug "$msg"
}
}