mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-15 21:03:23 +00:00
Compare commits
9 Commits
PROD_2016.
...
PROD_2016.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3c0c050b3a | ||
|
|
a1b375c929 | ||
|
|
929f8e1a44 | ||
|
|
ad50582e92 | ||
|
|
6de6704502 | ||
|
|
a5bc475cc1 | ||
|
|
c7396349f1 | ||
|
|
089cc1a5dd | ||
|
|
ef29820fa1 |
@@ -1,8 +1,7 @@
|
|||||||
/**
|
/**
|
||||||
* Iris Smart Fob
|
* ZigBee Button
|
||||||
*
|
*
|
||||||
* Copyright 2015 Mitch Pond
|
* Copyright 2015 Mitch Pond
|
||||||
* Presence code adapted from SmartThings Arrival Sensor HA device type
|
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
* 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:
|
* in compliance with the License. You may obtain a copy of the License at:
|
||||||
@@ -14,181 +13,235 @@
|
|||||||
* for the specific language governing permissions and limitations under the License.
|
* for the specific language governing permissions and limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "ZigBee Button", namespace: "smartthings", author: "Mitch Pond") {
|
definition (name: "ZigBee Button", namespace: "smartthings", author: "Mitch Pond") {
|
||||||
capability "Battery"
|
capability "Actuator"
|
||||||
capability "Button"
|
capability "Battery"
|
||||||
|
capability "Button"
|
||||||
capability "Configuration"
|
capability "Configuration"
|
||||||
capability "Presence Sensor"
|
capability "Refresh"
|
||||||
capability "Sensor"
|
capability "Sensor"
|
||||||
|
|
||||||
//fingerprint endpointId: "01", profileId: "0104", inClusters: "0000,0001,0003,0007,0020,0B05", outClusters: "0003,0006,0019", model:"3450-L", manufacturer: "CentraLite"
|
command "enrollResponse"
|
||||||
}
|
|
||||||
|
|
||||||
preferences{
|
fingerprint inClusters: "0000, 0001, 0003, 0020, 0402, 0B05", outClusters: "0003, 0006, 0008, 0019", manufacturer: "OSRAM", model: "LIGHTIFY Dimming Switch", deviceJoinName: "OSRAM LIGHTIFY Dimming Switch"
|
||||||
input ("holdTime", "number", title: "Minimum time in seconds for a press to count as \"held\"",
|
//fingerprint inClusters: "0000, 0001, 0003, 0020, 0500", outClusters: "0003,0019", manufacturer: "CentraLite", model: "3455-L", deviceJoinName: "Iris Care Pendant"
|
||||||
defaultValue: 3, displayDuringSetup: false)
|
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0402, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model: "3460-L", deviceJoinName: "Iris Smart Button"
|
||||||
input "checkInterval", "enum", title: "Presence timeout (minutes)",
|
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model:"3450-L", deviceJoinName: "Iris KeyFob"
|
||||||
defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
|
|
||||||
input "logging", "bool", title: "Enable debug logging",
|
|
||||||
defaultValue: false, displayDuringSetup: false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
tiles(scale: 2) {
|
simulator {}
|
||||||
standardTile("presence", "device.presence", width: 4, height: 4, canChangeBackground: true) {
|
|
||||||
state "present", label: "Present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0"
|
|
||||||
state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff"
|
|
||||||
}
|
|
||||||
standardTile("button", "device.button", decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "default", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
|
|
||||||
}
|
|
||||||
valueTile("battery", "device.battery", decoration: "flat", width: 2, height: 2) {
|
|
||||||
state "battery", label:'${currentValue}% battery', unit:""
|
|
||||||
}
|
|
||||||
|
|
||||||
main (["presence"])
|
preferences {
|
||||||
details(["presence","button","battery"])
|
section {
|
||||||
}
|
input ("holdTime", "number", title: "Minimum time in seconds for a press to count as \"held\"", defaultValue: 1, displayDuringSetup: false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tiles {
|
||||||
|
standardTile("button", "device.button", width: 2, height: 2) {
|
||||||
|
state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
|
||||||
|
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
|
||||||
|
}
|
||||||
|
|
||||||
|
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
|
||||||
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
|
}
|
||||||
|
|
||||||
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
|
||||||
|
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
|
}
|
||||||
|
main (["button"])
|
||||||
|
details(["button", "battery", "refresh"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
def descMap = zigbee.parseDescriptionAsMap(description)
|
log.debug "description is $description"
|
||||||
logIt descMap
|
def event = zigbee.getEvent(description)
|
||||||
state.lastCheckin = now()
|
if (event) {
|
||||||
logIt "lastCheckin = ${state.lastCheckin}"
|
sendEvent(event)
|
||||||
handlePresenceEvent(true)
|
}
|
||||||
|
else {
|
||||||
|
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
|
||||||
|
def descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
|
if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
|
||||||
|
event = getBatteryResult(zigbee.convertHexToInt(descMap.value))
|
||||||
|
}
|
||||||
|
else if (descMap.clusterInt == 0x0006 || descMap.clusterInt == 0x0008) {
|
||||||
|
event = parseNonIasButtonMessage(descMap)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (description?.startsWith('zone status')) {
|
||||||
|
event = parseIasButtonMessage(description)
|
||||||
|
}
|
||||||
|
|
||||||
def results = []
|
log.debug "Parse returned $event"
|
||||||
if (description?.startsWith('catchall:'))
|
def result = event ? createEvent(event) : []
|
||||||
results = parseCatchAllMessage(descMap)
|
|
||||||
else if (description?.startsWith('read attr -'))
|
|
||||||
results = parseReportAttributeMessage(descMap)
|
|
||||||
else logIt(descMap, "trace")
|
|
||||||
|
|
||||||
return results;
|
if (description?.startsWith('enroll request')) {
|
||||||
|
List cmds = enrollResponse()
|
||||||
|
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseIasButtonMessage(String description) {
|
||||||
|
int zoneInt = Integer.parseInt((description - "zone status 0x"), 16)
|
||||||
|
if (zoneInt & 0x02) {
|
||||||
|
resultMap = getButtonResult('press')
|
||||||
|
} else {
|
||||||
|
resultMap = getButtonResult('release')
|
||||||
|
}
|
||||||
|
|
||||||
|
return resultMap
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getBatteryResult(rawValue) {
|
||||||
|
log.debug 'Battery'
|
||||||
|
def volts = rawValue / 10
|
||||||
|
if (volts > 3.0 || volts == 0 || rawValue == 0xFF) {
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
def result = [
|
||||||
|
name: 'battery'
|
||||||
|
]
|
||||||
|
def minVolts = 2.1
|
||||||
|
def maxVolts = 3.0
|
||||||
|
def pct = (volts - minVolts) / (maxVolts - minVolts)
|
||||||
|
result.value = Math.min(100, (int) pct * 100)
|
||||||
|
def linkText = getLinkText(device)
|
||||||
|
result.descriptionText = "${linkText} battery was ${result.value}%"
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map parseNonIasButtonMessage(Map descMap){
|
||||||
|
def buttonState = ""
|
||||||
|
def buttonNumber = 0
|
||||||
|
if (((device.getDataValue("model") == "3460-L") || (device.getDataValue("model") == "3450-L"))
|
||||||
|
&&(descMap.clusterInt == 0x0006)) {
|
||||||
|
if (descMap.command == "01") {
|
||||||
|
getButtonResult("press")
|
||||||
|
}
|
||||||
|
else if (descMap.command == "00") {
|
||||||
|
getButtonResult("release")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (descMap.clusterInt == 0x0006) {
|
||||||
|
buttonState = "pushed"
|
||||||
|
if (descMap.command == "01") {
|
||||||
|
buttonNumber = 1
|
||||||
|
}
|
||||||
|
else if (descMap.command == "00") {
|
||||||
|
buttonNumber = 2
|
||||||
|
}
|
||||||
|
if (buttonNumber !=0) {
|
||||||
|
def descriptionText = "$device.displayName button $buttonNumber was $buttonState"
|
||||||
|
return createEvent(name: "button", value: buttonState, data: [buttonNumber: buttonNumber], descriptionText: descriptionText, isStateChange: true)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (descMap.clusterInt == 0x0008) {
|
||||||
|
if (descMap.command == "05") {
|
||||||
|
state.buttonNumber = 1
|
||||||
|
getButtonResult("press", 1)
|
||||||
|
}
|
||||||
|
else if (descMap.command == "01") {
|
||||||
|
state.buttonNumber = 2
|
||||||
|
getButtonResult("press", 2)
|
||||||
|
}
|
||||||
|
else if (descMap.command == "03") {
|
||||||
|
getButtonResult("release", state.buttonNumber)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def refresh() {
|
||||||
|
log.debug "Refreshing Battery"
|
||||||
|
|
||||||
|
return zigbee.readAttribute(0x0001, 0x20) +
|
||||||
|
zigbee.enrollResponse()
|
||||||
|
}
|
||||||
|
|
||||||
|
def configure() {
|
||||||
|
log.debug "Configuring Reporting, IAS CIE, and Bindings."
|
||||||
|
def cmds = []
|
||||||
|
if (device.getDataValue("model") == "3450-L") {
|
||||||
|
cmds << [
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 300",
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 2 1 6 {${device.zigbeeId}} {}", "delay 300",
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 3 1 6 {${device.zigbeeId}} {}", "delay 300",
|
||||||
|
"zdo bind 0x${device.deviceNetworkId} 4 1 6 {${device.zigbeeId}} {}", "delay 300"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
return zigbee.onOffConfig() +
|
||||||
|
zigbee.levelConfig() +
|
||||||
|
zigbee.configureReporting(0x0001, 0x20, 0x20, 30, 21600, 0x01) +
|
||||||
|
zigbee.enrollResponse() +
|
||||||
|
zigbee.readAttribute(0x0001, 0x20) +
|
||||||
|
cmds
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Map getButtonResult(buttonState, buttonNumber = 1) {
|
||||||
|
if (buttonState == 'release') {
|
||||||
|
log.debug "Button was value : $buttonState"
|
||||||
|
def timeDiff = now() - state.pressTime
|
||||||
|
log.info "timeDiff: $timeDiff"
|
||||||
|
def holdPreference = holdTime ?: 1
|
||||||
|
log.info "holdp1 : $holdPreference"
|
||||||
|
holdPreference = (holdPreference as int) * 1000
|
||||||
|
log.info "holdp2 : $holdPreference"
|
||||||
|
if (timeDiff > 10000) { //timeDiff>10sec check for refresh sending release value causing actions to be executed
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if (timeDiff < holdPreference) {
|
||||||
|
buttonState = "pushed"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
buttonState = "held"
|
||||||
|
}
|
||||||
|
def descriptionText = "$device.displayName button $buttonNumber was $buttonState"
|
||||||
|
return createEvent(name: "button", value: buttonState, data: [buttonNumber: buttonNumber], descriptionText: descriptionText, isStateChange: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (buttonState == 'press') {
|
||||||
|
log.debug "Button was value : $buttonState"
|
||||||
|
state.pressTime = now()
|
||||||
|
log.info "presstime: ${state.pressTime}"
|
||||||
|
return [:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
def installed() {
|
||||||
|
initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
def updated() {
|
def updated() {
|
||||||
startTimer()
|
initialize()
|
||||||
configure()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure(){
|
def initialize() {
|
||||||
logIt "Configuring Smart Fob..."
|
if ((device.getDataValue("manufacturer") == "OSRAM") && (device.getDataValue("model") == "LIGHTIFY Dimming Switch")) {
|
||||||
[
|
sendEvent(name: "numberOfButtons", value: 2)
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 200",
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 2 1 6 {${device.zigbeeId}} {}", "delay 200",
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 3 1 6 {${device.zigbeeId}} {}", "delay 200",
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 4 1 6 {${device.zigbeeId}} {}", "delay 200",
|
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}", "delay 200"
|
|
||||||
] +
|
|
||||||
zigbee.configureReporting(0x0001,0x0020,0x20,20,20,0x01)
|
|
||||||
}
|
|
||||||
|
|
||||||
def parseCatchAllMessage(descMap) {
|
|
||||||
if (descMap?.clusterId == "0006" && descMap?.command == "01") //button pressed
|
|
||||||
handleButtonPress(descMap.sourceEndpoint as int)
|
|
||||||
else if (descMap?.clusterId == "0006" && descMap?.command == "00") //button released
|
|
||||||
handleButtonRelease(descMap.sourceEndpoint as int)
|
|
||||||
else logIt("Parse: Unhandled message: ${descMap}","trace")
|
|
||||||
}
|
|
||||||
|
|
||||||
def parseReportAttributeMessage(descMap) {
|
|
||||||
if (descMap?.cluster == "0001" && descMap?.attrId == "0020") createBatteryEvent(getBatteryLevel(descMap.value))
|
|
||||||
else logIt descMap
|
|
||||||
}
|
|
||||||
|
|
||||||
private createBatteryEvent(percent) {
|
|
||||||
logIt "Battery level at " + percent
|
|
||||||
return createEvent([name: "battery", value: percent])
|
|
||||||
}
|
|
||||||
|
|
||||||
//this method determines if a press should count as a push or a hold and returns the relevant event type
|
|
||||||
private handleButtonRelease(button) {
|
|
||||||
logIt "lastPress state variable: ${state.lastPress}"
|
|
||||||
def sequenceError = {logIt("Uh oh...missed a message? Dropping this event.", "error"); state.lastPress = null; return []}
|
|
||||||
|
|
||||||
if (!state.lastPress) return sequenceError()
|
|
||||||
else if (state.lastPress.button != button) return sequenceError()
|
|
||||||
|
|
||||||
def currentTime = now()
|
|
||||||
def startOfPress = state.lastPress?.time
|
|
||||||
def timeDif = currentTime - startOfPress
|
|
||||||
def holdTimeMillisec = (settings.holdTime?:3).toInteger() * 1000
|
|
||||||
|
|
||||||
state.lastPress = null //we're done with this. clear it to make error conditions easier to catch
|
|
||||||
|
|
||||||
if (timeDif < 0)
|
|
||||||
//likely a message sequence issue or dropped packet. Drop this press and wait for another.
|
|
||||||
return sequenceError()
|
|
||||||
else if (timeDif < holdTimeMillisec)
|
|
||||||
return createButtonEvent(button,"pushed")
|
|
||||||
else
|
|
||||||
return createButtonEvent(button,"held")
|
|
||||||
}
|
|
||||||
|
|
||||||
private handleButtonPress(button) {
|
|
||||||
state.lastPress = [button: button, time: now()]
|
|
||||||
}
|
|
||||||
|
|
||||||
private createButtonEvent(button,action) {
|
|
||||||
logIt "Button ${button} ${action}"
|
|
||||||
return createEvent([
|
|
||||||
name: "button",
|
|
||||||
value: action,
|
|
||||||
data:[buttonNumber: button],
|
|
||||||
descriptionText: "${device.displayName} button ${button} was ${action}",
|
|
||||||
isStateChange: true,
|
|
||||||
displayed: true])
|
|
||||||
}
|
|
||||||
|
|
||||||
private getBatteryLevel(rawValue) {
|
|
||||||
def intValue = Integer.parseInt(rawValue,16)
|
|
||||||
def min = 2.1
|
|
||||||
def max = 3.0
|
|
||||||
def vBatt = intValue / 10
|
|
||||||
return ((vBatt - min) / (max - min) * 100) as int
|
|
||||||
}
|
|
||||||
|
|
||||||
private handlePresenceEvent(present) {
|
|
||||||
def wasPresent = device.currentState("presence")?.value == "present"
|
|
||||||
if (!wasPresent && present) {
|
|
||||||
logIt "Sensor is present"
|
|
||||||
startTimer()
|
|
||||||
} else if (!present) {
|
|
||||||
logIt "Sensor is not present"
|
|
||||||
stopTimer()
|
|
||||||
}
|
}
|
||||||
def linkText = getLinkText(device)
|
else if ((device.getDataValue("manufacturer") == "CentraLite") &&
|
||||||
def eventMap = [
|
((device.getDataValue("model") == "3455-L") || (device.getDataValue("model") == "3460-L"))) {
|
||||||
name: "presence",
|
sendEvent(name: "numberOfButtons", value: 1)
|
||||||
value: present ? "present" : "not present",
|
|
||||||
linkText: linkText,
|
|
||||||
descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}",
|
|
||||||
]
|
|
||||||
logIt "Creating presence event: ${eventMap}"
|
|
||||||
sendEvent(eventMap)
|
|
||||||
}
|
|
||||||
|
|
||||||
private startTimer() {
|
|
||||||
logIt "Scheduling periodic timer"
|
|
||||||
schedule("0 * * * * ?", checkPresenceCallback)
|
|
||||||
}
|
|
||||||
|
|
||||||
private stopTimer() {
|
|
||||||
logIt "Stopping periodic timer"
|
|
||||||
unschedule()
|
|
||||||
}
|
|
||||||
|
|
||||||
def checkPresenceCallback() {
|
|
||||||
def timeSinceLastCheckin = (now() - state.lastCheckin) / 1000
|
|
||||||
def theCheckInterval = (checkInterval ? checkInterval as int : 2) * 60
|
|
||||||
logIt "Sensor checked in ${timeSinceLastCheckin} seconds ago"
|
|
||||||
if (timeSinceLastCheckin >= theCheckInterval) {
|
|
||||||
handlePresenceEvent(false)
|
|
||||||
}
|
}
|
||||||
|
else if ((device.getDataValue("manufacturer") == "CentraLite") && (device.getDataValue("model") == "3450-L")) {
|
||||||
|
sendEvent(name: "numberOfButtons", value: 4)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//default. can be changed
|
||||||
|
sendEvent(name: "numberOfButtons", value: 4)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ****** Utility functions ******
|
|
||||||
|
|
||||||
private logIt(str, logLevel = 'debug') {if (settings.logging) log."$logLevel"(str) }
|
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2015 SmartThings
|
* Copyright 2016 SmartThings
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
|
* 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:
|
* in compliance with the License. You may obtain a copy of the License at:
|
||||||
@@ -11,100 +11,133 @@
|
|||||||
* for the specific language governing permissions and limitations under the License.
|
* for the specific language governing permissions and limitations under the License.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
/*
|
|
||||||
* Capabilities
|
|
||||||
* - Battery
|
|
||||||
* - Configuration
|
|
||||||
* - Refresh
|
|
||||||
* - Switch
|
|
||||||
* - Valve
|
|
||||||
*/
|
|
||||||
|
|
||||||
metadata {
|
metadata {
|
||||||
definition (name: "Zigbee Valve", namespace: "smartthings", author: "SmartThings") {
|
definition (name: "ZigBee Valve", namespace: "smartthings", author: "SmartThings") {
|
||||||
capability "Battery"
|
capability "Actuator"
|
||||||
capability "Configuration"
|
capability "Battery"
|
||||||
capability "Refresh"
|
capability "Configuration"
|
||||||
capability "Switch"
|
capability "Power Source"
|
||||||
capability "Valve"
|
capability "Refresh"
|
||||||
|
capability "Valve"
|
||||||
|
|
||||||
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0020,0006,0B02", outClusters: "0003"
|
fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0006, 0020, 0B02, FC02", outClusters: "0019", manufacturer: "WAXMAN", model: "leakSMART Water Valve v2.10", deviceJoinName: "leakSMART Valve"
|
||||||
}
|
fingerprint profileId: "0104", inClusters: "0000, 0001, 0003, 0004, 0005, 0006, 0008, 000F, 0020, 0B02", outClusters: "0003, 0019", manufacturer: "WAXMAN", model: "House Water Valve - MDL-TBD", deviceJoinName: "Waxman House Water Valve"
|
||||||
|
}
|
||||||
|
|
||||||
// simulator metadata
|
// simulator metadata
|
||||||
simulator {
|
simulator {
|
||||||
// status messages
|
// status messages
|
||||||
status "on": "on/off: 1"
|
status "on": "on/off: 1"
|
||||||
status "off": "on/off: 0"
|
status "off": "on/off: 0"
|
||||||
|
|
||||||
// reply messages
|
// reply messages
|
||||||
reply "zcl on-off on": "on/off: 1"
|
reply "zcl on-off on": "on/off: 1"
|
||||||
reply "zcl on-off off": "on/off: 0"
|
reply "zcl on-off off": "on/off: 0"
|
||||||
}
|
}
|
||||||
|
|
||||||
// UI tile definitions
|
tiles(scale: 2) {
|
||||||
tiles {
|
multiAttributeTile(name:"valve", type: "generic", width: 6, height: 4, canChangeIcon: true){
|
||||||
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
|
tileAttribute ("device.contact", key: "PRIMARY_CONTROL") {
|
||||||
state "off", label: 'closed', action: "switch.on", icon: "st.Outdoor.outdoor16", backgroundColor: "#e86d13"
|
attributeState "open", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#53a7c0", nextState:"closing"
|
||||||
state "on", label: 'open', action: "switch.off", icon: "st.Outdoor.outdoor16", backgroundColor: "#53a7c0"
|
attributeState "closed", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#e86d13", nextState:"opening"
|
||||||
}
|
attributeState "opening", label: '${name}', action: "valve.close", icon: "st.valves.water.open", backgroundColor: "#53a7c0", nextState:"closing"
|
||||||
main "switch"
|
attributeState "closing", label: '${name}', action: "valve.open", icon: "st.valves.water.closed", backgroundColor: "#e86d13", nextState:"opening"
|
||||||
details(["switch"])
|
}
|
||||||
}
|
tileAttribute ("powerSource", key: "SECONDARY_CONTROL") {
|
||||||
|
attributeState "powerSource", label:'Power Source: ${currentValue}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
valueTile("battery", "device.battery", inactiveLabel:false, decoration:"flat", width:2, height:2) {
|
||||||
|
state "battery", label:'${currentValue}% battery', unit:""
|
||||||
|
}
|
||||||
|
|
||||||
|
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
|
||||||
|
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
|
||||||
|
}
|
||||||
|
|
||||||
|
main(["valve"])
|
||||||
|
details(["valve", "battery", "refresh"])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private getCLUSTER_BASIC() { 0x0000 }
|
||||||
|
private getBASIC_ATTR_POWER_SOURCE() { 0x0007 }
|
||||||
|
private getCLUSTER_POWER() { 0x0001 }
|
||||||
|
private getPOWER_ATTR_BATTERY_PERCENTAGE_REMAINING() { 0x0021 }
|
||||||
|
private getTYPE_U8() { 0x20 }
|
||||||
|
private getTYPE_ENUM8() { 0x30 }
|
||||||
|
|
||||||
// Parse incoming device messages to generate events
|
// Parse incoming device messages to generate events
|
||||||
def parse(String description) {
|
def parse(String description) {
|
||||||
log.info description
|
log.debug "description is $description"
|
||||||
if (description?.startsWith("catchall:")) {
|
def event = zigbee.getEvent(description)
|
||||||
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
|
if (event) {
|
||||||
def result = createEvent(name: name, value: value)
|
if(event.name == "switch") {
|
||||||
def msg = zigbee.parse(description)
|
event.name = "contact" //0006 cluster in valve is tied to contact
|
||||||
log.debug "Parse returned ${result?.descriptionText}"
|
if(event.value == "on") {
|
||||||
return result
|
event.value = "open"
|
||||||
log.trace msg
|
}
|
||||||
log.trace "data: $msg.data"
|
else if(event.value == "off") {
|
||||||
}
|
event.value = "closed"
|
||||||
else {
|
}
|
||||||
def name = description?.startsWith("on/off: ") ? "switch" : null
|
}
|
||||||
def value = name == "switch" ? (description?.endsWith(" 1") ? "on" : "off") : null
|
sendEvent(event)
|
||||||
def result = createEvent(name: name, value: value)
|
}
|
||||||
log.debug "Parse returned ${result?.descriptionText}"
|
else {
|
||||||
return result
|
def descMap = zigbee.parseDescriptionAsMap(description)
|
||||||
}
|
if (descMap.clusterInt == CLUSTER_BASIC && descMap.attrInt == BASIC_ATTR_POWER_SOURCE){
|
||||||
}
|
def value = descMap.value
|
||||||
|
if (value == "01" || value == "02") {
|
||||||
// Commands to device
|
sendEvent(name: "powerSource", value: "Mains")
|
||||||
def on() {
|
}
|
||||||
log.debug "on()"
|
else if (value == "03") {
|
||||||
sendEvent(name: "switch", value: "on")
|
sendEvent(name: "powerSource", value: "Battery")
|
||||||
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
|
}
|
||||||
}
|
else if (value == "04") {
|
||||||
|
sendEvent(name: "powerSource", value: "DC")
|
||||||
def off() {
|
}
|
||||||
log.debug "off()"
|
else {
|
||||||
sendEvent(name: "switch", value: "off")
|
sendEvent(name: "powerSource", value: "Unknown")
|
||||||
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
|
}
|
||||||
|
}
|
||||||
|
else if (descMap.clusterInt == CLUSTER_POWER && descMap.attrInt == POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) {
|
||||||
|
event.name = "battery"
|
||||||
|
event.value = Math.round(Integer.parseInt(descMap.value, 16) / 2)
|
||||||
|
sendEvent(event)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
log.warn "DID NOT PARSE MESSAGE for description : $description"
|
||||||
|
log.debug descMap
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def open() {
|
def open() {
|
||||||
log.debug "on()"
|
zigbee.on()
|
||||||
sendEvent(name: "switch", value: "on")
|
|
||||||
"st cmd 0x${device.deviceNetworkId} 1 6 1 {}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def close() {
|
def close() {
|
||||||
log.debug "off()"
|
zigbee.off()
|
||||||
sendEvent(name: "switch", value: "off")
|
|
||||||
"st cmd 0x${device.deviceNetworkId} 1 6 0 {}"
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def refresh() {
|
def refresh() {
|
||||||
log.debug "sending refresh command"
|
log.debug "refresh called"
|
||||||
"st rattr 0x${device.deviceNetworkId} 1 6 0"
|
zigbee.onOffRefresh() +
|
||||||
|
zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_POWER_SOURCE) +
|
||||||
|
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING) +
|
||||||
|
zigbee.onOffConfig() +
|
||||||
|
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING, TYPE_U8, 600, 21600, 1) +
|
||||||
|
zigbee.configureReporting(CLUSTER_BASIC, BASIC_ATTR_POWER_SOURCE, TYPE_ENUM8, 5, 21600, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
def configure() {
|
def configure() {
|
||||||
|
log.debug "Configuring Reporting and Bindings."
|
||||||
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}"
|
zigbee.onOffConfig() +
|
||||||
|
zigbee.configureReporting(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING, TYPE_U8, 600, 21600, 1) +
|
||||||
|
zigbee.configureReporting(CLUSTER_BASIC, BASIC_ATTR_POWER_SOURCE, TYPE_ENUM8, 5, 21600, 1) +
|
||||||
|
zigbee.onOffRefresh() +
|
||||||
|
zigbee.readAttribute(CLUSTER_BASIC, BASIC_ATTR_POWER_SOURCE) +
|
||||||
|
zigbee.readAttribute(CLUSTER_POWER, POWER_ATTR_BATTERY_PERCENTAGE_REMAINING)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,17 +24,24 @@ definition(
|
|||||||
iconX3Url: "http://www.gidjit.com/appicon@3x.png",
|
iconX3Url: "http://www.gidjit.com/appicon@3x.png",
|
||||||
oauth: [displayName: "Gidjit", displayLink: "www.gidjit.com"])
|
oauth: [displayName: "Gidjit", displayLink: "www.gidjit.com"])
|
||||||
|
|
||||||
|
preferences(oauthPage: "deviceAuthorization") {
|
||||||
|
// deviceAuthorization page is simply the devices to authorize
|
||||||
|
page(name: "deviceAuthorization", title: "Device Authorization", nextPage: "instructionPage",
|
||||||
|
install: false, uninstall: true) {
|
||||||
|
section ("Allow Gidjit to have access, thereby allowing you to quickly control and monitor your following devices. Privacy Policy can be found at http://priv.gidjit.com/privacy.html") {
|
||||||
|
input "switches", "capability.switch", title: "Control/Monitor your switches", multiple: true, required: false
|
||||||
|
input "thermostats", "capability.thermostat", title: "Control/Monitor your thermostats", multiple: true, required: false
|
||||||
|
input "windowShades", "capability.windowShade", title: "Control/Monitor your window shades", multiple: true, required: false //windowShade
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
preferences {
|
page(name: "instructionPage", title: "Device Discovery", install: true) {
|
||||||
section ("Allow Gidjit to have access, there by allowing you to quickly control and monitor the following devices") {
|
section() {
|
||||||
input "switches", "capability.switch", title: "Control/Monitor your switches", multiple: true, required: false
|
paragraph "Now the process is complete return to the Devices section of the Detected Screen. From there and you can add actions to each of your device panels, including launching SmartThings routines."
|
||||||
input "thermostats", "capability.thermostat", title: "Control/Monitor your thermostats", multiple: true, required: false
|
}
|
||||||
input "windowShades", "capability.windowShade", title: "Control/Monitor your window shades", multiple: true, required: false //windowShade
|
}
|
||||||
//input "bulbs", "capability.colorControl", title: "Control your lights", multiple: true, required: false //windowShade
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
mappings {
|
mappings {
|
||||||
path("/structureinfo") {
|
path("/structureinfo") {
|
||||||
action: [
|
action: [
|
||||||
|
|||||||
Reference in New Issue
Block a user