mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-09 13:21:53 +00:00
Compare commits
1 Commits
PROD_2016.
...
MSA-1436-1
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
554ff50ec8 |
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ metadata {
|
||||
capability "Temperature Measurement"
|
||||
capability "Water Sensor"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
|
||||
@@ -22,7 +22,6 @@ metadata {
|
||||
capability "Temperature Measurement"
|
||||
capability "Refresh"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
capability "Refresh"
|
||||
capability "Temperature Measurement"
|
||||
capability "Health Check"
|
||||
capability "Sensor"
|
||||
|
||||
command "enrollResponse"
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
metadata {
|
||||
definition (name: "Simulated Alarm", namespace: "smartthings/testing", author: "SmartThings") {
|
||||
capability "Alarm"
|
||||
capability "Sensor"
|
||||
capability "Actuator"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
metadata {
|
||||
definition (name: "Simulated Color Control", namespace: "smartthings/testing", author: "SmartThings") {
|
||||
capability "Color Control"
|
||||
capability "Sensor"
|
||||
capability "Actuator"
|
||||
}
|
||||
|
||||
simulator {
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user