Compare commits

..

1 Commits

Author SHA1 Message Date
Trish Farris
554ff50ec8 MSA-1436: Yale Locks 2016-08-09 16:05:07 -05:00
32 changed files with 1038 additions and 846 deletions

View File

@@ -64,10 +64,8 @@ metadata {
}
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff"
state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff"
state "off", label: '${name}', action: "switch.on", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff"
state "on", label: '${name}', action: "switch.off", icon: "st.Electronics.electronics16", backgroundColor: "#79b821"
}
valueTile("1", "device.station1", decoration: "flat", canChangeIcon: false) {
state "station1", label:'${currentValue}', action:"preset1"
@@ -749,16 +747,8 @@ def cb_boseSetInput(xml, input) {
*/
def boseSetPowerState(boolean enable) {
log.info "boseSetPowerState(${enable})"
// Fix to get faster update of power status back from speaker after sending on/off
// Instead of queuing the command to be sent after the refresh send it directly via sendHubCommand
// Note: This is a temporary hack that should be replaced by a re-design of the
// DTH to use sendHubCommand for all commands
sendHubCommand(bosePOST("/key", "<key state=\"press\" sender=\"Gabbo\">POWER</key>"))
sendHubCommand(bosePOST("/key", "<key state=\"release\" sender=\"Gabbo\">POWER</key>"))
sendHubCommand(boseGET("/now_playing"))
if (enable) {
queueCallback('nowPlaying', "cb_boseConfirmPowerOn", 5)
}
queueCallback('nowPlaying', "cb_boseSetPowerState", enable ? "POWERON" : "POWEROFF")
return boseRefreshNowPlaying()
}
/**
@@ -797,11 +787,10 @@ def cb_boseSetPowerState(xml, state) {
*/
def cb_boseConfirmPowerOn(xml, tries) {
def result = []
def attempt = tries as Integer
log.warn "boseConfirmPowerOn() attempt #$attempt"
if (xml.attributes()['source'] == "STANDBY" && attempt > 0) {
log.warn "boseConfirmPowerOn() attempt #" + tries
if (xml.attributes()['source'] == "STANDBY" && tries > 0) {
result << boseRefreshNowPlaying()
queueCallback('nowPlaying', "cb_boseConfirmPowerOn", attempt-1)
queueCallback('nowPlaying', "cb_boseConfirmPowerOn", tries-1)
}
return result
}

View File

@@ -43,7 +43,7 @@ metadata {
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset To White", action:"reset", icon:"st.lights.philips.hue-single"
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -51,7 +51,7 @@ metadata {
}
main(["rich-control"])
details(["rich-control", "reset", "refresh"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
}
}
@@ -75,78 +75,118 @@ def parse(description) {
// handle commands
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(level)
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) {
log.trace parent.setLevel(this, percent)
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) {
log.trace parent.setSaturation(this, percent)
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
}
void setHue(percent) {
log.debug "Executing 'setHue'"
if (verifyPercent(percent)) {
log.trace parent.setHue(this, percent)
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
}
void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = []
def validValues = [:]
if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!validValues.isEmpty()) {
log.trace parent.setColor(this, validValues)
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
}
void reset() {
log.debug "Executing 'reset'"
def value = [hue:20, saturation:2]
setAdjustedColor(value)
def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
setColor(adjusted)
} else {
log.warn "Invalid color input $value"
log.warn "Invalid color input"
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
}
}
@@ -155,6 +195,22 @@ void refresh() {
parent.manualRefresh()
}
def adjustOutgoingHue(percent) {
def adjusted = percent
if (percent > 31) {
if (percent < 63.0) {
adjusted = percent + (7 * (percent -30 ) / 32)
}
else if (percent < 73.0) {
adjusted = 69 + (5 * (percent - 62) / 10)
}
else {
adjusted = percent + (2 * (100 - percent) / 28)
}
}
log.info "percent: $percent, adjusted: $adjusted"
adjusted
}
def verifyPercent(percent) {
if (percent == null)

View File

@@ -7,13 +7,8 @@
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
attribute "serialNumber", "string"
attribute "networkAddress", "string"
// Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network
// Possible values "Online" or "Offline"
attribute "status", "string"
// Id is the number on the back of the hub, Hue uses last six digits of Mac address
// This is also used in the Hue application as ID
attribute "idNumber", "string"
}
simulator {
@@ -22,23 +17,22 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"rich-control"){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
attributeState "Offline", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#ffffff"
attributeState "Online", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#79b821"
tileAttribute ("", key: "PRIMARY_CONTROL") {
attributeState "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#F3C200"
}
tileAttribute ("serialNumber", key: "SECONDARY_CONTROL") {
attributeState "default", label:'SN: ${currentValue}'
}
valueTile("doNotRemove", "v", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
state "default", label:'Do not remove'
}
valueTile("idNumber", "device.idNumber", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
state "default", label:'ID: ${currentValue}'
valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) {
state "default", label:'SN: ${currentValue}'
}
valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
state "default", label:'IP: ${currentValue}'
valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 4, inactiveLabel: false) {
state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false
}
main (["rich-control"])
details(["rich-control", "idNumber", "networkAddress", "doNotRemove"])
details(["rich-control", "networkAddress"])
}
}

View File

@@ -43,16 +43,16 @@ metadata {
}
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: 'WHITES'
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset To White", action:"reset", icon:"st.lights.philips.hue-single"
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -84,86 +84,118 @@ def parse(description) {
// handle commands
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(level)
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) {
log.trace parent.setLevel(this, percent)
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
sendEvent(name: "switch", value: "on")
}
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) {
log.trace parent.setSaturation(this, percent)
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
}
void setHue(percent) {
log.debug "Executing 'setHue'"
if (verifyPercent(percent)) {
log.trace parent.setHue(this, percent)
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
}
void setColor(value) {
log.debug "setColor: ${value}, $this"
def events = []
def validValues = [:]
if (verifyPercent(value.hue)) {
events << createEvent(name: "hue", value: value.hue, displayed: false)
validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
events << createEvent(name: "saturation", value: value.saturation, displayed: false)
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
events << createEvent(name: "color", value: value.hex)
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
events << createEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
events << createEvent(name: "switch", value: "off")
validValues.switch = "off"
} else {
events << createEvent(name: "switch", value: "on")
validValues.switch = "on"
}
if (!validValues.isEmpty()) {
log.trace parent.setColor(this, validValues)
if (!events.isEmpty()) {
parent.setColor(this, validValues)
}
events.each {
sendEvent(it)
}
}
void reset() {
log.debug "Executing 'reset'"
setColorTemperature(4000)
def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
setColor(adjusted)
} else {
log.warn "Invalid color input $value"
log.warn "Invalid color input"
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
log.trace parent.setColorTemperature(this, value)
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature $value"
log.warn "Invalid color temperature"
}
}
@@ -172,6 +204,23 @@ void refresh() {
parent.manualRefresh()
}
def adjustOutgoingHue(percent) {
def adjusted = percent
if (percent > 31) {
if (percent < 63.0) {
adjusted = percent + (7 * (percent -30 ) / 32)
}
else if (percent < 73.0) {
adjusted = 69 + (5 * (percent - 62) / 10)
}
else {
adjusted = percent + (2 * (100 - percent) / 28)
}
}
log.info "percent: $percent, adjusted: $adjusted"
adjusted
}
def verifyPercent(percent) {
if (percent == null)
return false

View File

@@ -68,16 +68,20 @@ def parse(description) {
// handle commands
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) {
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "switch", value: "on")
} else {
log.warn "$percent is not 0-100"
}

View File

@@ -36,12 +36,12 @@ metadata {
}
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2200..6500)") {
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: 'WHITES'
state "colorTemperature", label: '${currentValue} K'
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
@@ -73,16 +73,20 @@ def parse(description) {
// handle commands
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) {
log.trace parent.setLevel(this, percent)
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "switch", value: "on")
} else {
log.warn "$percent is not 0-100"
}
@@ -91,7 +95,9 @@ void setLevel(percent) {
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
log.trace parent.setColorTemperature(this, value)
parent.setColorTemperature(this, value)
sendEvent(name: "colorTemperature", value: value)
sendEvent(name: "switch", value: "on")
} else {
log.warn "Invalid color temperature"
}
@@ -101,3 +107,4 @@ void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}

View File

@@ -22,7 +22,6 @@ metadata {
capability "Temperature Measurement"
capability "Water Sensor"
capability "Health Check"
capability "Sensor"
command "enrollResponse"

View File

@@ -22,7 +22,6 @@ metadata {
capability "Temperature Measurement"
capability "Refresh"
capability "Health Check"
capability "Sensor"
command "enrollResponse"

View File

@@ -24,7 +24,6 @@
capability "Refresh"
capability "Temperature Measurement"
capability "Health Check"
capability "Sensor"
command "enrollResponse"
}

View File

@@ -21,7 +21,6 @@ metadata {
capability "Temperature Measurement"
capability "Relative Humidity Measurement"
capability "Health Check"
capability "Sensor"
fingerprint endpointId: "01", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003"
}

View File

@@ -16,8 +16,6 @@
metadata {
definition (name: "Simulated Alarm", namespace: "smartthings/testing", author: "SmartThings") {
capability "Alarm"
capability "Sensor"
capability "Actuator"
}
simulator {

View File

@@ -1,8 +1,6 @@
metadata {
definition (name: "Simulated Color Control", namespace: "smartthings/testing", author: "SmartThings") {
capability "Color Control"
capability "Sensor"
capability "Actuator"
}
simulator {

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here.
definition (name: "Simulated Contact Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Contact Sensor"
capability "Sensor"
command "open"
command "close"

View File

@@ -15,8 +15,6 @@ metadata {
// Automatically generated. Make future change here.
definition (name: "Simulated Lock", namespace: "smartthings/testing", author: "bob") {
capability "Lock"
capability "Sensor"
capability "Actuator"
}
// Simulated lock

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here.
definition (name: "Simulated Motion Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Motion Sensor"
capability "Sensor"
command "active"
command "inactive"

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here.
definition (name: "Simulated Presence Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Presence Sensor"
capability "Sensor"
command "arrived"
command "departed"

View File

@@ -16,8 +16,6 @@ metadata {
definition (name: "Simulated Switch", namespace: "smartthings/testing", author: "bob") {
capability "Switch"
capability "Relay Switch"
capability "Sensor"
capability "Actuator"
command "onPhysical"
command "offPhysical"

View File

@@ -16,7 +16,6 @@ metadata {
definition (name: "Simulated Temperature Sensor", namespace: "smartthings/testing", author: "SmartThings") {
capability "Temperature Measurement"
capability "Switch Level"
capability "Sensor"
command "up"
command "down"

View File

@@ -16,8 +16,6 @@ metadata {
definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") {
capability "Thermostat"
capability "Relative Humidity Measurement"
capability "Sensor"
capability "Actuator"
command "tempUp"
command "tempDown"

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here.
definition (name: "Simulated Water Sensor", namespace: "smartthings/testing", author: "SmartThings") {
capability "Water Sensor"
capability "Sensor"
command "wet"
command "dry"

View File

@@ -53,10 +53,7 @@ def parse(String description) {
def event = zigbee.getEvent(description)
if (event) {
if (event.name=="level" && event.value==0) {}
else {
sendEvent(event)
}
sendEvent(event)
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -28,9 +28,6 @@ metadata {
capability "Switch"
capability "Switch Level"
attribute "colorName", "string"
command "setGenericName"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY LED FLEXIBLE STRIP RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Flex RGBW", deviceJoinName: "OSRAM LIGHTIFY LED FLEXIBLE STRIP RGBW"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY A19 RGBW", deviceJoinName: "OSRAM LIGHTIFY LED A19 RGBW"
@@ -57,15 +54,15 @@ metadata {
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2700..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorName", label: '${currentValue}'
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["switch"])
details(["switch", "colorTempSliderControl", "colorName", "refresh"])
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
}
}
@@ -81,16 +78,10 @@ private getATTRIBUTE_COLOR_TEMPERATURE() { 0x0007 }
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
log.debug event
if (event.name=="level" && event.value==0) {}
else {
if (event.name=="colorTemperature") {
setGenericName(event.value)
}
sendEvent(event)
}
def finalResult = zigbee.getEvent(description)
if (finalResult) {
log.debug finalResult
sendEvent(finalResult)
}
else {
def zigbeeMap = zigbee.parseDescriptionAsMap(description)
@@ -130,27 +121,9 @@ def configure() {
}
def setColorTemperature(value) {
setGenericName(value)
zigbee.setColorTemperature(value)
}
//Naming based on the wiki article here: http://en.wikipedia.org/wiki/Color_temperature
def setGenericName(value){
if (value != null) {
def genericName = "White"
if (value < 3300) {
genericName = "Soft White"
} else if (value < 4150) {
genericName = "Moonlight"
} else if (value <= 5000) {
genericName = "Cool White"
} else if (value >= 5000) {
genericName = "Daylight"
}
sendEvent(name: "colorName", value: genericName)
}
}
def setLevel(value) {
zigbee.setLevel(value)
}

View File

@@ -20,7 +20,6 @@ metadata {
capability "Switch"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0004, 0005, 0006"
fingerprint profileId: "0104", inClusters: "0000, 0003, 0006", outClusters: "0003, 0006, 0019, 0406", manufacturer: "Leviton", model: "ZSS-10", deviceJoinName: "Leviton Switch"
}
// simulator metadata

View File

@@ -49,6 +49,9 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
tileAttribute ("colorName", key: "SECONDARY_CONTROL") {
attributeState "colorName", label:'${currentValue}'
}
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
@@ -58,12 +61,12 @@ metadata {
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2700..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorName", "device.colorName", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorName", label: '${currentValue}'
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
main(["switch"])
details(["switch", "colorTempSliderControl", "colorName", "refresh"])
details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
}
}
@@ -72,13 +75,7 @@ def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
if (event.name=="level" && event.value==0) {}
else {
if (event.name=="colorTemperature") {
setGenericName(event.value)
}
sendEvent(event)
}
sendEvent(event)
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"

View File

@@ -0,0 +1,683 @@
/**
* Copyright 2015 SmartThings
*
* 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.
*
*/
metadata {
definition (name: "Z-Wave Lock Reporting", namespace: "smartthings", author: "SmartThings") {
capability "Actuator"
capability "Lock"
capability "Polling"
capability "Refresh"
capability "Sensor"
capability "Lock Codes"
capability "Battery"
command "unlockwtimeout"
fingerprint deviceId: "0x4003", inClusters: "0x98"
fingerprint deviceId: "0x4004", inClusters: "0x98"
}
simulator {
status "locked": "command: 9881, payload: 00 62 03 FF 00 00 FE FE"
status "unlocked": "command: 9881, payload: 00 62 03 00 00 00 FE FE"
reply "9881006201FF,delay 4200,9881006202": "command: 9881, payload: 00 62 03 FF 00 00 FE FE"
reply "988100620100,delay 4200,9881006202": "command: 9881, payload: 00 62 03 00 00 00 FE FE"
}
tiles(scale: 2) {
multiAttributeTile(name:"toggle", type: "generic", width: 6, height: 4){
tileAttribute ("device.lock", key: "PRIMARY_CONTROL") {
attributeState "locked", label:'locked', action:"lock.unlock", icon:"st.locks.lock.locked", backgroundColor:"#79b821", nextState:"unlocking"
attributeState "unlocked", label:'unlocked', action:"lock.lock", icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff", nextState:"locking"
attributeState "unknown", label:"unknown", action:"lock.lock", icon:"st.locks.lock.unknown", backgroundColor:"#ffffff", nextState:"locking"
attributeState "locking", label:'locking', icon:"st.locks.lock.locked", backgroundColor:"#79b821"
attributeState "unlocking", label:'unlocking', icon:"st.locks.lock.unlocked", backgroundColor:"#ffffff"
}
}
standardTile("lock", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'lock', action:"lock.lock", icon:"st.locks.lock.locked", nextState:"locking"
}
standardTile("unlock", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'unlock', action:"lock.unlock", icon:"st.locks.lock.unlocked", nextState:"unlocking"
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("refresh", "device.lock", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "toggle"
details(["toggle", "lock", "unlock", "battery", "refresh"])
}
}
import physicalgraph.zwave.commands.doorlockv1.*
import physicalgraph.zwave.commands.usercodev1.*
def updated() {
try {
if (!state.init) {
state.init = true
response(secureSequence([zwave.doorLockV1.doorLockOperationGet(), zwave.batteryV1.batteryGet()]))
}
} catch (e) {
log.warn "updated() threw $e"
}
}
def parse(String description) {
def result = null
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "This lock failed to complete the network security key exchange. If you are unable to control it via SmartThings, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [ 0x98: 1, 0x72: 2, 0x85: 2, 0x86: 1 ])
if (cmd) {
result = zwaveEvent(cmd)
}
}
log.debug "\"$description\" parsed to ${result.inspect()}"
result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x62: 1, 0x71: 2, 0x80: 1, 0x85: 2, 0x63: 1, 0x98: 1, 0x86: 1])
// log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful")
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
state.sec = cmd.commandClassSupport.collect { String.format("%02X ", it) }.join()
if (cmd.commandClassControl) {
state.secCon = cmd.commandClassControl.collect { String.format("%02X ", it) }.join()
}
log.debug "Security command classes: $state.sec"
createEvent(name:"secureInclusion", value:"success", descriptionText:"Lock is securely included")
}
def zwaveEvent(DoorLockOperationReport cmd) {
def result = []
def map = [ name: "lock" ]
if (cmd.doorLockMode == 0xFF) {
map.value = "locked"
} else if (cmd.doorLockMode >= 0x40) {
map.value = "unknown"
} else if (cmd.doorLockMode & 1) {
map.value = "unlocked with timeout"
} else {
map.value = "unlocked"
if (state.assoc != zwaveHubNodeId) {
log.debug "setting association"
result << response(secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)))
result << response(zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId))
result << response(secure(zwave.associationV1.associationGet(groupingIdentifier:1)))
}
}
result ? [createEvent(map), *result] : createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd) {
def result = []
def map = null
if (cmd.zwaveAlarmType == 6) {
if (1 <= cmd.zwaveAlarmEvent && cmd.zwaveAlarmEvent < 10) {
map = [ name: "lock", value: (cmd.zwaveAlarmEvent & 1) ? "locked" : "unlocked" ]
}
switch(cmd.zwaveAlarmEvent) {
case 1:
map.descriptionText = "$device.displayName was manually locked"
break
case 2:
map.descriptionText = "$device.displayName was manually unlocked"
break
case 5:
if (cmd.eventParameter) {
map.descriptionText = "$device.displayName was locked with code ${cmd.eventParameter.first()}"
map.data = [ usedCode: cmd.eventParameter[0] ]
}
break
case 6:
if (cmd.eventParameter) {
map.descriptionText = "$device.displayName was unlocked with code ${cmd.eventParameter.first()}"
map.data = [ usedCode: cmd.eventParameter[0] ]
}
break
case 9:
map.descriptionText = "$device.displayName was autolocked"
break
case 7:
case 8:
case 0xA:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName was not locked fully" ]
break
case 0xB:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName is jammed" ]
break
case 0xC:
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
allCodesDeleted()
break
case 0xD:
if (cmd.eventParameter) {
map = [ name: "codeReport", value: cmd.eventParameter[0], data: [ code: "" ], isStateChange: true ]
map.descriptionText = "$device.displayName code ${map.value} was deleted"
map.isStateChange = (state["code$map.value"] != "")
state["code$map.value"] = ""
} else {
map = [ name: "codeChanged", descriptionText: "$device.displayName: user code deleted", isStateChange: true ]
}
break
case 0xE:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName: user code added", isStateChange: true ]
if (cmd.eventParameter) {
map.value = cmd.eventParameter[0]
result << response(requestCode(cmd.eventParameter[0]))
}
break
case 0xF:
map = [ name: "codeChanged", descriptionText: "$device.displayName: user code not added, duplicate", isStateChange: true ]
break
case 0x10:
map = [ name: "tamper", value: "detected", descriptionText: "$device.displayName: keypad temporarily disabled", displayed: true ]
break
case 0x11:
map = [ descriptionText: "$device.displayName: keypad is busy" ]
break
case 0x12:
map = [ name: "codeChanged", descriptionText: "$device.displayName: program code changed", isStateChange: true ]
break
case 0x13:
map = [ name: "tamper", value: "detected", descriptionText: "$device.displayName: code entry attempt limit exceeded", displayed: true ]
break
default:
map = map ?: [ descriptionText: "$device.displayName: alarm event $cmd.zwaveAlarmEvent", displayed: false ]
break
}
} else if (cmd.zwaveAlarmType == 7) {
map = [ name: "tamper", value: "detected", displayed: true ]
switch (cmd.zwaveAlarmEvent) {
case 0:
map.value = "clear"
map.descriptionText = "$device.displayName: tamper alert cleared"
break
case 1:
case 2:
map.descriptionText = "$device.displayName: intrusion attempt detected"
break
case 3:
map.descriptionText = "$device.displayName: covering removed"
break
case 4:
map.descriptionText = "$device.displayName: invalid code"
break
default:
map.descriptionText = "$device.displayName: tamper alarm $cmd.zwaveAlarmEvent"
break
}
} else switch(cmd.alarmType) {
case 21: // Manually locked
case 18: // Locked with keypad
case 24: // Locked by command (Kwikset 914)
case 27: // Autolocked
map = [ name: "lock", value: "locked" ]
break
case 16: // Note: for levers this means it's unlocked, for non-motorized deadbolt, it's just unsecured and might not get unlocked
case 19:
map = [ name: "lock", value: "unlocked" ]
if (cmd.alarmLevel) {
map.descriptionText = "$device.displayName was unlocked with code $cmd.alarmLevel"
map.data = [ usedCode: cmd.alarmLevel ]
}
break
case 22:
case 25: // Kwikset 914 unlocked by command
map = [ name: "lock", value: "unlocked" ]
break
case 9:
case 17:
case 23:
case 26:
map = [ name: "lock", value: "unknown", descriptionText: "$device.displayName bolt is jammed" ]
break
case 13:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel was added", isStateChange: true ]
result << response(requestCode(cmd.alarmLevel))
break
case 32:
map = [ name: "codeChanged", value: "all", descriptionText: "$device.displayName: all user codes deleted", isStateChange: true ]
allCodesDeleted()
break
case 33:
map = [ name: "codeReport", value: cmd.alarmLevel, data: [ code: "" ], isStateChange: true ]
map.descriptionText = "$device.displayName code $cmd.alarmLevel was deleted"
map.isStateChange = (state["code$cmd.alarmLevel"] != "")
state["code$cmd.alarmLevel"] = ""
break
case 112:
map = [ name: "codeChanged", value: cmd.alarmLevel, descriptionText: "$device.displayName code $cmd.alarmLevel changed", isStateChange: true ]
result << response(requestCode(cmd.alarmLevel))
break
case 130: // Yale YRD batteries replaced
map = [ descriptionText: "$device.displayName batteries replaced", isStateChange: true ]
break
case 131:
map = [ /*name: "codeChanged", value: cmd.alarmLevel,*/ descriptionText: "$device.displayName code $cmd.alarmLevel is duplicate", isStateChange: false ]
break
case 161:
if (cmd.alarmLevel == 2) {
map = [ descriptionText: "$device.displayName front escutcheon removed", isStateChange: true ]
} else {
map = [ descriptionText: "$device.displayName detected failed user code attempt", isStateChange: true ]
}
break
case 167:
if (!state.lastbatt || now() - state.lastbatt > 12*60*60*1000) {
map = [ descriptionText: "$device.displayName: battery low", isStateChange: true ]
result << response(secure(zwave.batteryV1.batteryGet()))
} else {
map = [ name: "battery", value: device.currentValue("battery"), descriptionText: "$device.displayName: battery low", displayed: true ]
}
break
case 168:
map = [ name: "battery", value: 1, descriptionText: "$device.displayName: battery level critical", displayed: true ]
break
case 169:
map = [ name: "battery", value: 0, descriptionText: "$device.displayName: battery too low to operate lock", isStateChange: true ]
break
default:
map = [ displayed: false, descriptionText: "$device.displayName: alarm event $cmd.alarmType level $cmd.alarmLevel" ]
break
}
result ? [createEvent(map), *result] : createEvent(map)
}
def zwaveEvent(UserCodeReport cmd) {
def result = []
def name = "code$cmd.userIdentifier"
def code = cmd.code
def map = [:]
if (cmd.userIdStatus == UserCodeReport.USER_ID_STATUS_OCCUPIED ||
(cmd.userIdStatus == UserCodeReport.USER_ID_STATUS_STATUS_NOT_AVAILABLE && cmd.user && code != "**********"))
{
if (code == "**********") { // Schlage locks send us this instead of the real code
state.blankcodes = true
code = state["set$name"] ?: decrypt(state[name]) ?: code
state.remove("set$name".toString())
}
if (!code && cmd.userIdStatus == 1) { // Schlage touchscreen sends blank code to notify of a changed code
map = [ name: "codeChanged", value: cmd.userIdentifier, displayed: true, isStateChange: true ]
map.descriptionText = "$device.displayName code $cmd.userIdentifier " + (state[name] ? "changed" : "was added")
code = state["set$name"] ?: decrypt(state[name]) ?: "****"
state.remove("set$name".toString())
} else {
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: code ] ]
map.descriptionText = "$device.displayName code $cmd.userIdentifier is set"
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
map.isStateChange = true
}
result << createEvent(map)
} else {
map = [ name: "codeReport", value: cmd.userIdentifier, data: [ code: "" ] ]
if (state.blankcodes && state["reset$name"]) { // we deleted this code so we can tell that our new code gets set
map.descriptionText = "$device.displayName code $cmd.userIdentifier was reset"
map.displayed = map.isStateChange = true
result << createEvent(map)
state["set$name"] = state["reset$name"]
result << response(setCode(cmd.userIdentifier, state["reset$name"]))
state.remove("reset$name".toString())
} else {
if (state[name]) {
map.descriptionText = "$device.displayName code $cmd.userIdentifier was deleted"
} else {
map.descriptionText = "$device.displayName code $cmd.userIdentifier is not set"
}
map.displayed = (cmd.userIdentifier != state.requestCode && cmd.userIdentifier != state.pollCode)
map.isStateChange = true
result << createEvent(map)
}
code = ""
}
state[name] = code ? encrypt(code) : code
if (cmd.userIdentifier == state.requestCode) { // reloadCodes() was called, keep requesting the codes in order
if (state.requestCode + 1 > state.codes || state.requestCode >= 30) {
state.remove("requestCode") // done
} else {
state.requestCode = state.requestCode + 1 // get next
result << response(requestCode(state.requestCode))
}
}
if (cmd.userIdentifier == state.pollCode) {
if (state.pollCode + 1 > state.codes || state.pollCode >= 30) {
state.remove("pollCode") // done
} else {
state.pollCode = state.pollCode + 1
}
}
log.debug "code report parsed to ${result.inspect()}"
result
}
def zwaveEvent(UsersNumberReport cmd) {
def result = []
state.codes = cmd.supportedUsers
if (state.requestCode && state.requestCode <= cmd.supportedUsers) {
result << response(requestCode(state.requestCode))
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.associationv2.AssociationReport cmd) {
def result = []
if (cmd.nodeId.any { it == zwaveHubNodeId }) {
state.remove("associationQuery")
log.debug "$device.displayName is associated to $zwaveHubNodeId"
result << createEvent(descriptionText: "$device.displayName is associated")
state.assoc = zwaveHubNodeId
if (cmd.groupingIdentifier == 2) {
result << response(zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId))
}
} else if (cmd.groupingIdentifier == 1) {
result << response(secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)))
} else if (cmd.groupingIdentifier == 2) {
result << response(zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId))
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.timev1.TimeGet cmd) {
def result = []
def now = new Date().toCalendar()
if(location.timeZone) now.timeZone = location.timeZone
result << createEvent(descriptionText: "$device.displayName requested time update", displayed: false)
result << response(secure(zwave.timeV1.timeReport(
hourLocalTime: now.get(Calendar.HOUR_OF_DAY),
minuteLocalTime: now.get(Calendar.MINUTE),
secondLocalTime: now.get(Calendar.SECOND)))
)
result
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
// The old Schlage locks use group 1 for basic control - we don't want that, so unsubscribe from group 1
def result = [ createEvent(name: "lock", value: cmd.value ? "unlocked" : "locked") ]
result << response(zwave.associationV1.associationRemove(groupingIdentifier:1, nodeId:zwaveHubNodeId))
if (state.assoc != zwaveHubNodeId) {
result << response(zwave.associationV1.associationGet(groupingIdentifier:2))
}
result
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "$device.displayName has a low battery"
} else {
map.value = cmd.batteryLevel
}
state.lastbatt = now()
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
log.debug "msr: $msr"
updateDataValue("MSR", msr)
result << createEvent(descriptionText: "$device.displayName MSR: $msr", isStateChange: false)
result
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
def fw = "${cmd.applicationVersion}.${cmd.applicationSubVersion}"
updateDataValue("fw", fw)
if (state.MSR == "003B-6341-5044") {
updateDataValue("ver", "${cmd.applicationVersion >> 4}.${cmd.applicationVersion & 0xF}")
}
def text = "$device.displayName: firmware version: $fw, Z-Wave version: ${cmd.zWaveProtocolVersion}.${cmd.zWaveProtocolSubVersion}"
createEvent(descriptionText: text, isStateChange: false)
}
def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationBusy cmd) {
def msg = cmd.status == 0 ? "try again later" :
cmd.status == 1 ? "try again in $cmd.waitTime seconds" :
cmd.status == 2 ? "request queued" : "sorry"
createEvent(displayed: true, descriptionText: "$device.displayName is busy, $msg")
}
def zwaveEvent(physicalgraph.zwave.commands.applicationstatusv1.ApplicationRejectedRequest cmd) {
createEvent(displayed: true, descriptionText: "$device.displayName rejected the last request")
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
createEvent(displayed: false, descriptionText: "$device.displayName: $cmd")
}
def lockAndCheck(doorLockMode) {
secureSequence([
zwave.doorLockV1.doorLockOperationSet(doorLockMode: doorLockMode),
zwave.doorLockV1.doorLockOperationGet()
], 4200)
}
def lock() {
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_SECURED)
}
def unlock() {
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED)
}
def unlockwtimeout() {
lockAndCheck(DoorLockOperationSet.DOOR_LOCK_MODE_DOOR_UNSECURED_WITH_TIMEOUT)
}
def refresh() {
def cmds = [secure(zwave.doorLockV1.doorLockOperationGet())]
if (state.assoc == zwaveHubNodeId) {
log.debug "$device.displayName is associated to ${state.assoc}"
} else if (!state.associationQuery) {
log.debug "checking association"
cmds << "delay 4200"
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format() // old Schlage locks use group 2 and don't secure the Association CC
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = now()
} else if (secondsPast(state.associationQuery, 9)) {
cmds << "delay 6000"
cmds << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId).format()
cmds << secure(zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId))
cmds << zwave.associationV1.associationGet(groupingIdentifier:2).format()
cmds << secure(zwave.associationV1.associationGet(groupingIdentifier:1))
state.associationQuery = now()
}
log.debug "refresh sending ${cmds.inspect()}"
cmds
}
def poll() {
def cmds = []
// Only check lock state if it changed recently or we haven't had an update in an hour
def latest = device.currentState("lock")?.date?.time
if (!latest || !secondsPast(latest, 6 * 60) || secondsPast(state.lastPoll, 55 * 60)) {
cmds << secure(zwave.doorLockV1.doorLockOperationGet())
state.lastPoll = now()
} else if (!state.lastbatt || now() - state.lastbatt > 53*60*60*1000) {
cmds << secure(zwave.batteryV1.batteryGet())
state.lastbatt = now() //inside-214
}
if (cmds) {
log.debug "poll is sending ${cmds.inspect()}"
cmds
} else {
// workaround to keep polling from stopping due to lack of activity
sendEvent(descriptionText: "skipping poll", isStateChange: true, displayed: false)
null
}
reportAllCodes(state)
}
def requestCode(codeNumber) {
secure(zwave.userCodeV1.userCodeGet(userIdentifier: codeNumber))
}
def reloadAllCodes() {
def cmds = []
if (!state.codes) {
state.requestCode = 1
cmds << secure(zwave.userCodeV1.usersNumberGet())
} else {
if(!state.requestCode) state.requestCode = 1
cmds << requestCode(codeNumber)
}
cmds
}
def setCode(codeNumber, code) {
def strcode = code
log.debug "setting code $codeNumber to $code"
if (code instanceof String) {
code = code.toList().findResults { if(it > ' ' && it != ',' && it != '-') it.toCharacter() as Short }
} else {
strcode = code.collect{ it as Character }.join()
}
if (state.blankcodes) {
// Can't just set, we won't be able to tell if it was successful
if (state["code$codeNumber"] != "") {
if (state["setcode$codeNumber"] != strcode) {
state["resetcode$codeNumber"] = strcode
return deleteCode(codeNumber)
}
} else {
state["setcode$codeNumber"] = strcode
}
}
secureSequence([
zwave.userCodeV1.userCodeSet(userIdentifier:codeNumber, userIdStatus:1, user:code),
zwave.userCodeV1.userCodeGet(userIdentifier:codeNumber)
], 7000)
}
def deleteCode(codeNumber) {
log.debug "deleting code $codeNumber"
secureSequence([
zwave.userCodeV1.userCodeSet(userIdentifier:codeNumber, userIdStatus:0),
zwave.userCodeV1.userCodeGet(userIdentifier:codeNumber)
], 7000)
}
def updateCodes(codeSettings) {
if(codeSettings instanceof String) codeSettings = util.parseJson(codeSettings)
def set_cmds = []
def get_cmds = []
codeSettings.each { name, updated ->
def current = decrypt(state[name])
if (name.startsWith("code")) {
def n = name[4..-1].toInteger()
log.debug "$name was $current, set to $updated"
if (updated?.size() >= 4 && updated != current) {
def cmds = setCode(n, updated)
set_cmds << cmds.first()
get_cmds << cmds.last()
} else if ((current && updated == "") || updated == "0") {
def cmds = deleteCode(n)
set_cmds << cmds.first()
get_cmds << cmds.last()
} else if (updated && updated.size() < 4) {
// Entered code was too short
codeSettings["code$n"] = current
}
} else log.warn("unexpected entry $name: $updated")
}
if (set_cmds) {
return response(delayBetween(set_cmds, 2200) + ["delay 2200"] + delayBetween(get_cmds, 4200))
}
}
def getCode(codeNumber) {
decrypt(state["code$codeNumber"])
}
def getAllCodes() {
state.findAll { it.key.startsWith 'code' }.collectEntries {
[it.key, (it.value instanceof String && it.value.startsWith("~")) ? decrypt(it.value) : it.value]
}
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private secureSequence(commands, delay=4200) {
delayBetween(commands.collect{ secure(it) }, delay)
}
private Boolean secondsPast(timestamp, seconds) {
if (!(timestamp instanceof Number)) {
if (timestamp instanceof Date) {
timestamp = timestamp.time
} else if ((timestamp instanceof String) && timestamp.isNumber()) {
timestamp = timestamp.toLong()
} else {
return true
}
}
return (now() - timestamp) > (seconds * 1000)
}
private allCodesDeleted() {
if (state.codes instanceof Integer) {
(1..state.codes).each { n ->
if (state["code$n"]) {
result << createEvent(name: "codeReport", value: n, data: [ code: "" ], descriptionText: "code $n was deleted",
displayed: false, isStateChange: true)
}
state["code$n"] = ""
}
}
}
def reportAllCodes(state) {
def map = [ name: "reportAllCodes", data: [:], displayed: false, isStateChange: false, type: "physical" ]
state.each { entry ->
//iterate through all the state entries and add them to the event data to be handled by application event handlers
if ( entry.key ==~ /^code\d{1,}/ && entry.value.startsWith("~") ) {
map.data.put(entry.key, decrypt(entry.value))
} else {
map.data.put(entry.key, entry.value)
}
}
sendEvent(map)
}

View File

@@ -78,7 +78,7 @@ def humidityHandler(evt) {
log.debug "Notification already sent within the last ${deltaMinutes} minutes"
} else {
log.debug "Humidity Rose Above ${tooHumid}: sending SMS and activating ${mySwitch}"
log.debug "Humidity Rose Above ${tooHumid}: sending SMS to $phone1 and activating ${mySwitch}"
send("${humiditySensor1.label} sensed high humidity level of ${evt.value}")
switch1?.on()
}
@@ -91,7 +91,7 @@ def humidityHandler(evt) {
log.debug "Notification already sent within the last ${deltaMinutes} minutes"
} else {
log.debug "Humidity Fell Below ${notHumidEnough}: sending SMS and activating ${mySwitch}"
log.debug "Humidity Fell Below ${notHumidEnough}: sending SMS to $phone1 and activating ${mySwitch}"
send("${humiditySensor1.label} sensed high humidity level of ${evt.value}")
switch1?.off()
}

View File

@@ -25,11 +25,15 @@ preferences {
def installed() {
subscribe(contact1, "contact", contactHandler)
subscribe(switch1, "switch.on", switchOnHandler)
subscribe(switch1, "switch.off", switchOffHandler)
}
def updated() {
unsubscribe()
subscribe(contact1, "contact", contactHandler)
subscribe(switch1, "switch.on", switchOnHandler)
subscribe(switch1, "switch.off", switchOffHandler)
}
def contactHandler(evt) {
@@ -42,4 +46,4 @@ def contactHandler(evt) {
if (evt.value == "closed") {
if(state.wasOn)switch1.on()
}
}
}

View File

@@ -77,7 +77,7 @@ def humidityHandler(evt) {
} else {
if (state.lastStatus != "off") {
log.debug "Humidity Rose Above $humidityHigh1: sending SMS and deactivating $mySwitch"
log.debug "Humidity Rose Above $humidityHigh1: sending SMS to $phone1 and deactivating $mySwitch"
send("${humiditySensor1.label} sensed high humidity level of ${evt.value}, turning off ${switch1.label}")
switch1?.off()
state.lastStatus = "off"
@@ -99,7 +99,7 @@ def humidityHandler(evt) {
} else {
if (state.lastStatus != "on") {
log.debug "Humidity Dropped Below $humidityLow1: sending SMS and activating $mySwitch"
log.debug "Humidity Dropped Below $humidityLow1: sending SMS to $phone1 and activating $mySwitch"
send("${humiditySensor1.label} sensed low humidity level of ${evt.value}, turning on ${switch1.label}")
switch1?.on()
state.lastStatus = "on"
@@ -125,4 +125,4 @@ private send(msg) {
}
log.debug msg
}
}

View File

@@ -68,7 +68,7 @@ def scheduleCheck()
sendNotificationToContacts("No one has fed the dog", recipients)
}
else {
log.debug "Feeder was not opened since $midnight, texting one phone number"
log.debug "Feeder was not opened since $midnight, texting $phone1"
sendSms(phone1, "No one has fed the dog")
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -69,10 +69,10 @@ def temperatureHandler(evt) {
def alreadySentSms = recentEvents.count { it.doubleValue <= tooCold } > 1
if (alreadySentSms) {
log.debug "SMS already sent within the last $deltaMinutes minutes"
log.debug "SMS already sent to $phone1 within the last $deltaMinutes minutes"
// TODO: Send "Temperature back to normal" SMS, turn switch off
} else {
log.debug "Temperature dropped below $tooCold: sending SMS and activating $mySwitch"
log.debug "Temperature dropped below $tooCold: sending SMS to $phone1 and activating $mySwitch"
def tempScale = location.temperatureScale ?: "F"
send("${temperatureSensor1.displayName} is too cold, reporting a temperature of ${evt.value}${evt.unit?:tempScale}")
switch1?.on()