diff --git a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy
index 744879d..108a72a 100644
--- a/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy
+++ b/devicetypes/smartthings/aeon-multisensor-6.src/aeon-multisensor-6.groovy
@@ -24,6 +24,8 @@ metadata {
capability "Battery"
attribute "tamper", "enum", ["detected", "clear"]
+ attribute "batteryStatus", "string"
+ attribute "powerSupply", "enum", ["USB Cable", "Battery"]
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
}
@@ -63,6 +65,19 @@ metadata {
status "wake up" : "command: 8407, payload: "
}
+ preferences {
+ input description: "Please consult AEOTEC MULTISENSOR 6 operating manual for advanced setting options. You can skip this configuration to use default settings",
+ title: "Advanced Configuration", displayDuringSetup: true, type: "paragraph", element: "paragraph"
+
+ input "motionDelayTime", "enum", title: "Motion Sensor Delay Time",
+ options: ["20 seconds", "40 seconds", "1 minute", "2 minutes", "3 minutes", "4 minutes"], defaultValue: "${motionDelayTime}", displayDuringSetup: true
+
+ input "motionSensitivity", "enum", title: "Motion Sensor Sensitivity", options: ["normal","maximum","minimum"], defaultValue: "${motionSensitivity}", displayDuringSetup: true
+
+ input "reportInterval", "enum", title: "Sensors Report Interval",
+ options: ["8 minutes", "15 minutes", "30 minutes", "1 hour", "6 hours", "12 hours", "18 hours", "24 hours"], defaultValue: "${reportInterval}", displayDuringSetup: true
+ }
+
tiles(scale: 2) {
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
@@ -85,53 +100,78 @@ metadata {
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
state "humidity", label:'${currentValue}% humidity', unit:""
}
+
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
- state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
+ state "illuminance", label:'${currentValue} ${unit}', unit:"lux"
}
+
+ valueTile("ultravioletIndex", "device.ultravioletIndex", inactiveLabel: false, width: 2, height: 2) {
+ state "ultravioletIndex", label:'${currentValue} UV index', unit:""
+ }
+
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
- main(["motion", "temperature", "humidity", "illuminance"])
- details(["motion", "temperature", "humidity", "illuminance", "battery"])
+ valueTile("batteryStatus", "device.batteryStatus", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
+ state "batteryStatus", label:'${currentValue}', unit:""
+ }
+
+ valueTile("powerSupply", "device.powerSupply", height: 2, width: 2, decoration: "flat") {
+ state "powerSupply", label:'${currentValue} powered', backgroundColor:"#ffffff"
+ }
+
+ main(["motion", "temperature", "humidity", "illuminance", "ultravioletIndex"])
+ details(["motion", "temperature", "humidity", "illuminance", "ultravioletIndex", "batteryStatus"])
}
}
-def updated()
-{
- if (state.sec && !isConfigured()) {
- // in case we miss the SCSR
+def updated() {
+ log.debug "Updated with settings: ${settings}"
+ log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
+
+ if (device.latestValue("powerSupply") == "USB Cable") { //case1: USB powered
response(configure())
+ } else if (device.latestValue("powerSupply") == "Battery") { //case2: battery powered
+ // setConfigured("false") is used by WakeUpNotification
+ setConfigured("false") //wait until the next time device wakeup to send configure command after user change preference
+ } else { //case3: power source is not identified, ask user to properly pair the sensor again
+ log.warn "power source is not identified, check it sensor is powered by USB, if so > configure()"
+ def request = []
+ request << zwave.configurationV1.configurationGet(parameterNumber: 101)
+ response(commands(request))
}
}
-def parse(String description)
-{
+def parse(String description) {
+ log.debug "parse() >> description: $description"
def result = null
if (description.startsWith("Err 106")) {
- state.sec = 0
+ log.debug "parse() >> Err 106"
result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true,
- descriptionText: "This sensor 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.")
+ descriptionText: "This sensor 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.")
} else if (description != "updated") {
+ log.debug "parse() >> zwave.parse(description)"
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1])
if (cmd) {
result = zwaveEvent(cmd)
}
}
- log.debug "Parsed '${description}' to ${result.inspect()}"
+ log.debug "After zwaveEvent(cmd) >> Parsed '${description}' to ${result.inspect()}"
return result
}
-def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd)
-{
+//this notification will be sent only when device is battery powered
+def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd) {
def result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)]
-
+ def cmds = []
if (!isConfigured()) {
- // we're still in the process of configuring a newly joined device
log.debug("late configure")
- result += response(configure())
+ result << response(configure())
} else {
- result += response(zwave.wakeUpV1.wakeUpNoMoreInformation())
+ log.debug("Device has been configured sending >> wakeUpNoMoreInformation()")
+ cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
+ result << response(cmds)
}
result
}
@@ -149,10 +189,29 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) {
- response(configure())
+ log.info "Executing zwaveEvent 98 (SecurityV1): 03 (SecurityCommandsSupportedReport) with cmd: $cmd"
+ state.sec = 1
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.securityv1.NetworkKeyVerify cmd) {
+ state.sec = 1
+ log.info "Executing zwaveEvent 98 (SecurityV1): 07 (NetworkKeyVerify) with cmd: $cmd (node is securely included)"
+ def result = [createEvent(name:"secureInclusion", value:"success", descriptionText:"Secure inclusion was successful", isStateChange: true)]
+ result
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
+ log.info "Executing zwaveEvent 72 (ManufacturerSpecificV2) : 05 (ManufacturerSpecificReport) with cmd: $cmd"
+ log.debug "manufacturerId: ${cmd.manufacturerId}"
+ log.debug "manufacturerName: ${cmd.manufacturerName}"
+ log.debug "productId: ${cmd.productId}"
+ log.debug "productTypeId: ${cmd.productTypeId}"
+ def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
+ updateDataValue("MSR", msr)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
+ def result = []
def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) {
map.value = 1
@@ -162,11 +221,14 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
map.value = cmd.batteryLevel
}
state.lastbatt = now()
- createEvent(map)
+ result << createEvent(map)
+ if (device.latestValue("powerSupply") != "USB Cable"){
+ result << createEvent(name: "batteryStatus", value: "${map.value} % battery", displayed: false)
+ }
+ result
}
-def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd)
-{
+def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd){
def map = [:]
switch (cmd.sensorType) {
case 1:
@@ -208,7 +270,6 @@ def motionEvent(value) {
}
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
- setConfigured()
motionEvent(cmd.sensorValue)
}
@@ -225,47 +286,112 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
result << createEvent(name: "tamper", value: "clear", displayed: false)
break
case 3:
- result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName was moved")
+ result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName was tampered")
break
case 7:
result << motionEvent(1)
break
}
} else {
+ log.warn "Need to handle this cmd.notificationType: ${cmd.notificationType}"
result << createEvent(descriptionText: cmd.toString(), isStateChange: false)
}
result
}
+def zwaveEvent(physicalgraph.zwave.commands.configurationv2.ConfigurationReport cmd) {
+ log.debug "ConfigurationReport: $cmd"
+ def result = []
+ def value
+ if (cmd.parameterNumber == 9 && cmd.configurationValue[0] == 0) {
+ value = "USB Cable"
+ if (!isConfigured()) {
+ log.debug("ConfigurationReport: configuring device")
+ result << response(configure())
+ }
+ result << createEvent(name: "batteryStatus", value: value, displayed: false)
+ result << createEvent(name: "powerSupply", value: value, displayed: false)
+ }else if (cmd.parameterNumber == 9 && cmd.configurationValue[0] == 1) {
+ value = "Battery"
+ result << createEvent(name: "powerSupply", value: value, displayed: false)
+ } else if (cmd.parameterNumber == 101){
+ result << response(configure())
+ }
+ result
+}
+
def zwaveEvent(physicalgraph.zwave.Command cmd) {
+ log.debug "General zwaveEvent cmd: ${cmd}"
createEvent(descriptionText: cmd.toString(), isStateChange: false)
}
def configure() {
// This sensor joins as a secure device if you double-click the button to include it
- if (device.device.rawDescription =~ /98/ && !state.sec) {
- log.debug "Multi 6 not sending configure until secure"
- return []
- }
- log.debug "Multi 6 configure()"
- def request = [
- // send no-motion report 20 seconds after motion stops
- zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: 20),
+ log.debug "${device.displayName} is configuring its settings"
+ def request = []
- // report every 8 minutes (threshold reports don't work on battery power)
- zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 8*60),
+ //1. set association groups for hub
+ request << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)
- // report automatically on threshold change
- zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 1),
+ request << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId)
+
+ //2. automatic report flags
+ // param 101 -103 [4 bytes] 128: light sensor, 64 humidity, 32 temperature sensor, 2 ultraviolet sensor, 1 battery sensor -> send command 227 to get all reports
+ request << zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 226) //association group 1
+
+ request << zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 1) //association group 2
+
+ //3. no-motion report x seconds after motion stops (default 20 secs)
+ request << zwave.configurationV1.configurationSet(parameterNumber: 3, size: 2, scaledConfigurationValue: timeOptionValueMap[motionDelayTime] ?: 20)
+
+ //4. motionSensitivity 3 levels: 64-normal (default), 127-maximum, 0-minimum
+ request << zwave.configurationV1.configurationSet(parameterNumber: 6, size: 1,
+ scaledConfigurationValue:
+ motionSensitivity == "normal" ? 64 :
+ motionSensitivity == "maximum" ? 127 :
+ motionSensitivity == "minimum" ? 0 : 64)
+
+ //5. report every x minutes (threshold reports don't work on battery power, default 8 mins)
+ request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: 8*60) //association group 1
+
+ request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6*60*60) //association group 2
+
+ //6. report automatically on threshold change
+ request << zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 1)
+
+ //7. query sensor data
+ request << zwave.batteryV1.batteryGet()
+ request << zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 0x0C) //motion
+ request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x01) //temperature
+ request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x03) //illuminance
+ request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x05) //humidity
+ request << zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 0x1B) //ultravioletIndex
+
+ setConfigured("true")
- zwave.batteryV1.batteryGet(),
- zwave.sensorBinaryV2.sensorBinaryGet(sensorType: 0x0C),
- ]
commands(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
}
-private setConfigured() {
- updateDataValue("configured", "true")
+private def getTimeOptionValueMap() { [
+ "20 seconds" : 20,
+ "40 seconds" : 40,
+ "1 minute" : 60,
+ "2 minutes" : 2*60,
+ "3 minutes" : 3*60,
+ "4 minutes" : 4*60,
+ "5 minutes" : 5*60,
+ "8 minutes" : 8*60,
+ "15 minutes" : 15*60,
+ "30 minutes" : 30*60,
+ "1 hours" : 1*60*60,
+ "6 hours" : 6*60*60,
+ "12 hours" : 12*60*60,
+ "18 hours" : 6*60*60,
+ "24 hours" : 24*60*60,
+]}
+
+private setConfigured(configure) {
+ updateDataValue("configured", configure)
}
private isConfigured() {
@@ -281,5 +407,6 @@ private command(physicalgraph.zwave.Command cmd) {
}
private commands(commands, delay=200) {
+ log.info "sending commands: ${commands}"
delayBetween(commands.collect{ command(it) }, delay)
}
diff --git a/devicetypes/smartthings/dimmer-switch.src/dimmer-switch.groovy b/devicetypes/smartthings/dimmer-switch.src/dimmer-switch.groovy
index 4218b3d..6b7eca1 100644
--- a/devicetypes/smartthings/dimmer-switch.src/dimmer-switch.groovy
+++ b/devicetypes/smartthings/dimmer-switch.src/dimmer-switch.groovy
@@ -55,141 +55,136 @@ metadata {
}
}
- standardTile("indicator", "device.indicatorStatus", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
+ standardTile("indicator", "device.indicatorStatus", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "when off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off"
state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on"
state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit"
}
- standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
- state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+
+ standardTile("refresh", "device.switch", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
+ state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ valueTile("level", "device.level", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
+ state "level", label:'${currentValue} %', unit:"%", backgroundColor:"#ffffff"
}
main(["switch"])
- details(["switch", "refresh", "indicator"])
+ details(["switch", "level", "indicator", "refresh"])
+
}
}
def parse(String description) {
- def item1 = [
- canBeCurrentState: false,
- linkText: getLinkText(device),
- isStateChange: false,
- displayed: false,
- descriptionText: description,
- value: description
- ]
- def result
- def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
- if (cmd) {
- result = createEvent(cmd, item1)
+ def result = null
+ if (description != "updated") {
+ log.debug "parse() >> zwave.parse($description)"
+ def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
+ if (cmd) {
+ result = zwaveEvent(cmd)
+ }
}
- else {
- item1.displayed = displayed(description, item1.isStateChange)
- result = [item1]
+ if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
+ result = [result, response(zwave.basicV1.basicGet())]
+ log.debug "Was hailed: requesting state update"
+ } else {
+ log.debug "Parse returned ${result?.descriptionText}"
}
- log.debug "Parse returned ${result?.descriptionText}"
- result
+ return result
}
-def createEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, Map item1) {
- def result = doCreateEvent(cmd, item1)
- for (int i = 0; i < result.size(); i++) {
- result[i].type = "physical"
+def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
+ dimmerEvents(cmd)
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd) {
+ dimmerEvents(cmd)
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd) {
+ dimmerEvents(cmd)
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd) {
+ dimmerEvents(cmd)
+}
+
+private dimmerEvents(physicalgraph.zwave.Command cmd) {
+ def value = (cmd.value ? "on" : "off")
+ def result = [createEvent(name: "switch", value: value)]
+ if (cmd.value && cmd.value <= 100) {
+ result << createEvent(name: "level", value: cmd.value, unit: "%")
}
- result
+ return result
}
-def createEvent(physicalgraph.zwave.commands.basicv1.BasicSet cmd, Map item1) {
- def result = doCreateEvent(cmd, item1)
- for (int i = 0; i < result.size(); i++) {
- result[i].type = "physical"
- }
- result
-}
-
-def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStartLevelChange cmd, Map item1) {
- []
-}
-
-def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd, Map item1) {
- [response(zwave.basicV1.basicGet())]
-}
-
-def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelSet cmd, Map item1) {
- def result = doCreateEvent(cmd, item1)
- for (int i = 0; i < result.size(); i++) {
- result[i].type = "physical"
- }
- result
-}
-
-def createEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelReport cmd, Map item1) {
- def result = doCreateEvent(cmd, item1)
- result[0].descriptionText = "${item1.linkText} is ${item1.value}"
- result[0].handlerName = cmd.value ? "statusOn" : "statusOff"
- for (int i = 0; i < result.size(); i++) {
- result[i].type = "digital"
- }
- result
-}
-
-def doCreateEvent(physicalgraph.zwave.Command cmd, Map item1) {
- def result = [item1]
-
- item1.name = "switch"
- item1.value = cmd.value ? "on" : "off"
- item1.handlerName = item1.value
- item1.descriptionText = "${item1.linkText} was turned ${item1.value}"
- item1.canBeCurrentState = true
- item1.isStateChange = isStateChange(device, item1.name, item1.value)
- item1.displayed = item1.isStateChange
-
- if (cmd.value >= 5) {
- def item2 = new LinkedHashMap(item1)
- item2.name = "level"
- item2.value = cmd.value as String
- item2.unit = "%"
- item2.descriptionText = "${item1.linkText} dimmed ${item2.value} %"
- item2.canBeCurrentState = true
- item2.isStateChange = isStateChange(device, item2.name, item2.value)
- item2.displayed = false
- result << item2
- }
- result
-}
def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
+ log.debug "ConfigurationReport $cmd"
def value = "when off"
if (cmd.configurationValue[0] == 1) {value = "when on"}
if (cmd.configurationValue[0] == 2) {value = "never"}
- [name: "indicatorStatus", value: value, display: false]
+ createEvent([name: "indicatorStatus", value: value])
}
-def createEvent(physicalgraph.zwave.Command cmd, Map map) {
- // Handles any Z-Wave commands we aren't interested in
- log.debug "UNHANDLED COMMAND $cmd"
+def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
+ createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false])
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
+ log.debug "manufacturerId: ${cmd.manufacturerId}"
+ log.debug "manufacturerName: ${cmd.manufacturerName}"
+ log.debug "productId: ${cmd.productId}"
+ log.debug "productTypeId: ${cmd.productTypeId}"
+ def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
+ updateDataValue("MSR", msr)
+ createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
+}
+
+def zwaveEvent(physicalgraph.zwave.commands.switchmultilevelv1.SwitchMultilevelStopLevelChange cmd) {
+ [createEvent(name:"switch", value:"on"), response(zwave.switchMultilevelV1.switchMultilevelGet().format())]
+}
+
+def zwaveEvent(physicalgraph.zwave.Command cmd) {
+ // Handles all Z-Wave commands we aren't interested in
+ [:]
}
def on() {
- log.info "on"
- delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
+ delayBetween([
+ zwave.basicV1.basicSet(value: 0xFF).format(),
+ zwave.switchMultilevelV1.switchMultilevelGet().format()
+ ],5000)
}
def off() {
- delayBetween ([zwave.basicV1.basicSet(value: 0x00).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
+ delayBetween([
+ zwave.basicV1.basicSet(value: 0x00).format(),
+ zwave.switchMultilevelV1.switchMultilevelGet().format()
+ ],5000)
}
def setLevel(value) {
+ log.debug "setLevel >> value: $value"
def valueaux = value as Integer
- def level = Math.min(valueaux, 99)
+ def level = Math.max(Math.min(valueaux, 99), 0)
+ if (level > 0) {
+ sendEvent(name: "switch", value: "on")
+ } else {
+ sendEvent(name: "switch", value: "off")
+ }
+ sendEvent(name: "level", value: level, unit: "%")
delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
}
def setLevel(value, duration) {
+ log.debug "setLevel >> value: $value, duration: $duration"
def valueaux = value as Integer
- def level = Math.min(valueaux, 99)
+ def level = Math.max(Math.min(valueaux, 99), 0)
def dimmingDuration = duration < 128 ? duration : 128 + Math.round(duration / 60)
- zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format()
+ def getStatusDelay = duration < 128 ? (duration*1000)+2000 : (Math.round(duration / 60)*60*1000)+2000
+ delayBetween ([zwave.switchMultilevelV2.switchMultilevelSet(value: level, dimmingDuration: dimmingDuration).format(),
+ zwave.switchMultilevelV1.switchMultilevelGet().format()], getStatusDelay)
}
def poll() {
@@ -197,21 +192,27 @@ def poll() {
}
def refresh() {
- zwave.switchMultilevelV1.switchMultilevelGet().format()
+ log.debug "refresh() is called"
+ def commands = []
+ commands << zwave.switchMultilevelV1.switchMultilevelGet().format()
+ if (getDataValue("MSR") == null) {
+ commands << zwave.manufacturerSpecificV1.manufacturerSpecificGet().format()
+ }
+ delayBetween(commands,100)
}
def indicatorWhenOn() {
- sendEvent(name: "indicatorStatus", value: "when on", display: false)
+ sendEvent(name: "indicatorStatus", value: "when on")
zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()
}
def indicatorWhenOff() {
- sendEvent(name: "indicatorStatus", value: "when off", display: false)
+ sendEvent(name: "indicatorStatus", value: "when off")
zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()
}
def indicatorNever() {
- sendEvent(name: "indicatorStatus", value: "never", display: false)
+ sendEvent(name: "indicatorStatus", value: "never")
zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()
}
@@ -222,4 +223,4 @@ def invertSwitch(invert=true) {
else {
zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format()
}
-}
+}
\ No newline at end of file
diff --git a/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy b/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
index b5f9f5c..1bd78f3 100644
--- a/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
+++ b/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
@@ -25,6 +25,8 @@ metadata {
capability "Refresh"
capability "Sensor"
+ attribute "currentIP", "string"
+
command "subscribe"
command "resubscribe"
command "unsubscribe"
@@ -34,21 +36,36 @@ metadata {
// simulator metadata
simulator {}
- // UI tile definitions
- tiles {
- standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
- state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
- state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
- state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
- state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
- }
- standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
- }
+ // UI tile definitions
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
+ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
+ attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
- main "switch"
- details (["switch", "refresh"])
- }
+ standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
+ state "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ state "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ state "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
+ }
+
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ main(["switch"])
+ details(["rich-control", "refresh"])
+ }
}
// parse events into attributes
@@ -68,6 +85,7 @@ def parse(String description) {
def result = []
def bodyString = msg.body
if (bodyString) {
+ unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) {
@@ -78,13 +96,14 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "Notify: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}")
} else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
} else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "GetBinaryResponse: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
+ def dispaux = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux)
}
}
@@ -101,14 +120,6 @@ private getCallBackAddress() {
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
-private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
-}
-
-private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
-}
-
private getHostAddress() {
def ip = getDataValue("ip")
def port = getDataValue("port")
@@ -195,6 +206,8 @@ def subscribe(ip, port) {
if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip"
updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
}
if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port"
@@ -259,6 +272,8 @@ User-Agent: CyberGarage-HTTP/1.0
def poll() {
log.debug "Executing 'poll'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
@@ -274,3 +289,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+
+def setOffline() {
+ sendEvent(name: "switch", value: "offline", descriptionText: "The device is offline")
+}
+
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
+}
+
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+}
diff --git a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
index eb3ea10..9649db4 100644
--- a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
+++ b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
@@ -21,6 +21,8 @@
capability "Refresh"
capability "Sensor"
+ attribute "currentIP", "string"
+
command "subscribe"
command "resubscribe"
command "unsubscribe"
@@ -31,17 +33,30 @@
}
// UI tile definitions
- tiles {
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "motion", canChangeIcon: true){
+ tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
+ attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
+ attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
+ attributeState "offline", label:'${name}', icon:"st.motion.motion.active", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
+
standardTile("motion", "device.motion", width: 2, height: 2) {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
- }
- standardTile("refresh", "device.motion", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
+ state("offline", label:'${name}', icon:"st.motion.motion.inactive", backgroundColor:"#ff0000")
}
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
main "motion"
- details (["motion", "refresh"])
+ details (["rich-control", "refresh"])
}
}
@@ -62,6 +77,7 @@ def parse(String description) {
def result = []
def bodyString = msg.body
if (bodyString) {
+ unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) {
@@ -72,7 +88,7 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "active" : "inactive"
log.debug "Notify - BinaryState = ${value}"
- result << createEvent(name: "motion", value: value)
+ result << createEvent(name: "motion", value: value, descriptionText: "Motion is ${value}")
} else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
}
@@ -91,14 +107,6 @@ private getCallBackAddress() {
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
-private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
-}
-
-private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
-}
-
private getHostAddress() {
def ip = getDataValue("ip")
def port = getDataValue("port")
@@ -125,6 +133,8 @@ def refresh() {
////////////////////////////
def getStatus() {
log.debug "Executing WeMo Motion 'getStatus'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
@@ -165,7 +175,9 @@ def subscribe(ip, port) {
def existingPort = getDataValue("port")
if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip"
- updateDataValue("ip", ip)
+ updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
}
if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port"
@@ -226,3 +238,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+
+def setOffline() {
+ sendEvent(name: "motion", value: "offline", descriptionText: "The device is offline")
+}
+
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
+}
+
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+}
diff --git a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
index b385ceb..cd9e0ec 100644
--- a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
+++ b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
@@ -10,120 +10,142 @@
* 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.
*
- * Wemo Switch
+ * Wemo Switch
*
- * Author: superuser
- * Date: 2013-10-11
+ * Author: Juan Risso (SmartThings)
+ * Date: 2015-10-11
*/
metadata {
- definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") {
- capability "Actuator"
- capability "Switch"
- capability "Polling"
- capability "Refresh"
- capability "Sensor"
+ definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") {
+ capability "Actuator"
+ capability "Switch"
+ capability "Polling"
+ capability "Refresh"
+ capability "Sensor"
- command "subscribe"
- command "resubscribe"
- command "unsubscribe"
- }
+ attribute "currentIP", "string"
- // simulator metadata
- simulator {}
+ command "subscribe"
+ command "resubscribe"
+ command "unsubscribe"
+ }
- // UI tile definitions
- tiles {
- standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
- state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821"
- state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff"
- }
- standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
- }
+ // simulator metadata
+ simulator {}
- main "switch"
- details (["switch", "refresh"])
- }
+ // UI tile definitions
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
+ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
+ attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
+
+ standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
+ state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
+ }
+
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ main(["switch"])
+ details(["rich-control", "refresh"])
+ }
}
// parse events into attributes
def parse(String description) {
- log.debug "Parsing '${description}'"
+ log.debug "Parsing '${description}'"
- def msg = parseLanMessage(description)
- def headerString = msg.header
+ def msg = parseLanMessage(description)
+ def headerString = msg.header
- if (headerString?.contains("SID: uuid:")) {
- def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
- sid -= "SID: uuid:".trim()
+ if (headerString?.contains("SID: uuid:")) {
+ def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
+ sid -= "SID: uuid:".trim()
- updateDataValue("subscriptionId", sid)
- }
+ updateDataValue("subscriptionId", sid)
+ }
- def result = []
- def bodyString = msg.body
- if (bodyString) {
- def body = new XmlSlurper().parseText(bodyString)
-
- if (body?.property?.TimeSyncRequest?.text()) {
- log.trace "Got TimeSyncRequest"
- result << timeSyncResponse()
- } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
- log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
- } else if (body?.property?.BinaryState?.text()) {
- def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
- log.trace "Notify: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
- } else if (body?.property?.TimeZoneNotification?.text()) {
- log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
- } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
- def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
- log.trace "GetBinaryResponse: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
- }
- }
-
- result
+ def result = []
+ def bodyString = msg.body
+ if (bodyString) {
+ unschedule("setOffline")
+ def body = new XmlSlurper().parseText(bodyString)
+ if (body?.property?.TimeSyncRequest?.text()) {
+ log.trace "Got TimeSyncRequest"
+ result << timeSyncResponse()
+ } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
+ log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
+ } else if (body?.property?.BinaryState?.text()) {
+ def value = body?.property?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
+ log.trace "Notify: BinaryState = ${value}, ${body.property.BinaryState}"
+ def dispaux = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux)
+ } else if (body?.property?.TimeZoneNotification?.text()) {
+ log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
+ } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
+ def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
+ log.trace "GetBinaryResponse: BinaryState = ${value}, ${body.property.BinaryState}"
+ log.info "Connection: ${device.currentValue("connection")}"
+ if (device.currentValue("currentIP") == "Offline") {
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "IP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
+ }
+ def dispaux2 = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux2)
+ }
+ }
+ result
}
private getTime() {
- // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
- ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
+ // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
+ ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
}
private getCallBackAddress() {
- device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
+ device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
+ Integer.parseInt(hex,16)
}
private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
private getHostAddress() {
- def ip = getDataValue("ip")
- def port = getDataValue("port")
-
- if (!ip || !port) {
- def parts = device.deviceNetworkId.split(":")
- if (parts.length == 2) {
- ip = parts[0]
- port = parts[1]
- } else {
- log.warn "Can't figure out ip and port for device: ${device.id}"
- }
- }
- log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
- return convertHexToIP(ip) + ":" + convertHexToInt(port)
+ def ip = getDataValue("ip")
+ def port = getDataValue("port")
+ if (!ip || !port) {
+ def parts = device.deviceNetworkId.split(":")
+ if (parts.length == 2) {
+ ip = parts[0]
+ port = parts[1]
+ } else {
+ log.warn "Can't figure out ip and port for device: ${device.id}"
+ }
+ }
+ log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
+ return convertHexToIP(ip) + ":" + convertHexToInt(port)
}
-
def on() {
- log.debug "Executing 'on'"
- sendEvent(name: "switch", value: "on")
+log.debug "Executing 'on'"
def turnOn = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()}
@@ -133,17 +155,16 @@ Content-Length: 333
-
+
1
-
+
""", physicalgraph.device.Protocol.LAN)
}
def off() {
- log.debug "Executing 'off'"
- sendEvent(name: "switch", value: "off")
- def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
+log.debug "Executing 'off'"
+def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()}
Content-Type: text/xml
@@ -152,36 +173,13 @@ Content-Length: 333
-
+
0
-
+
""", physicalgraph.device.Protocol.LAN)
}
-/*def refresh() {
- log.debug "Executing 'refresh'"
-new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
-SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
-Content-Length: 277
-Content-Type: text/xml; charset="utf-8"
-HOST: ${getHostAddress()}
-User-Agent: CyberGarage-HTTP/1.0
-
-
-
-
-
-
-
-""", physicalgraph.device.Protocol.LAN)
-}*/
-
-def refresh() {
- log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
- [subscribe(), timeSyncResponse(), poll()]
-}
-
def subscribe(hostAddress) {
log.debug "Executing 'subscribe()'"
def address = getCallBackAddress()
@@ -200,27 +198,30 @@ def subscribe() {
subscribe(getHostAddress())
}
-def subscribe(ip, port) {
- def existingIp = getDataValue("ip")
- def existingPort = getDataValue("port")
- if (ip && ip != existingIp) {
- log.debug "Updating ip from $existingIp to $ip"
- updateDataValue("ip", ip)
- }
- if (port && port != existingPort) {
- log.debug "Updating port from $existingPort to $port"
- updateDataValue("port", port)
- }
+def refresh() {
+ log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
+ [subscribe(), timeSyncResponse(), poll()]
+}
+def subscribe(ip, port) {
+ def existingIp = getDataValue("ip")
+ def existingPort = getDataValue("port")
+ if (ip && ip != existingIp) {
+ log.debug "Updating ip from $existingIp to $ip"
+ updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
+ }
+ if (port && port != existingPort) {
+ log.debug "Updating port from $existingPort to $port"
+ updateDataValue("port", port)
+ }
subscribe("${ip}:${port}")
}
-////////////////////////////
def resubscribe() {
-log.debug "Executing 'resubscribe()'"
-
-def sid = getDeviceDataByName("subscriptionId")
-
+ log.debug "Executing 'resubscribe()'"
+ def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1
HOST: ${getHostAddress()}
SID: uuid:${sid}
@@ -228,12 +229,11 @@ TIMEOUT: Second-5400
""", physicalgraph.device.Protocol.LAN)
-
}
-////////////////////////////
+
def unsubscribe() {
-def sid = getDeviceDataByName("subscriptionId")
+ def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""UNSUBSCRIBE publisher path HTTP/1.1
HOST: ${getHostAddress()}
SID: uuid:${sid}
@@ -242,7 +242,7 @@ SID: uuid:${sid}
""", physicalgraph.device.Protocol.LAN)
}
-////////////////////////////
+
//TODO: Use UTC Timezone
def timeSyncResponse() {
log.debug "Executing 'timeSyncResponse()'"
@@ -267,9 +267,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+def setOffline() {
+ //sendEvent(name: "currentIP", value: "Offline", displayed: false)
+ sendEvent(name: "switch", value: "offline", descriptionText: "The device is offline")
+}
def poll() {
log.debug "Executing 'poll'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
diff --git a/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy
new file mode 100644
index 0000000..dfe7475
--- /dev/null
+++ b/devicetypes/smartthings/zigbee-white-color-temperature-bulb.src/zigbee-white-color-temperature-bulb.groovy
@@ -0,0 +1,130 @@
+/**
+ * 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.
+ *
+ * ZigBee White Color Temperature Bulb
+ *
+ * Author: SmartThings
+ * Date: 2015-09-22
+ */
+
+metadata {
+ definition (name: "ZigBee White Color Temperature Bulb", namespace: "smartthings", author: "SmartThings") {
+
+ capability "Actuator"
+ capability "Color Temperature"
+ capability "Configuration"
+ capability "Refresh"
+ capability "Sensor"
+ capability "Switch"
+ capability "Switch Level"
+
+ attribute "colorName", "string"
+ command "setGenericName"
+
+ fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04", outClusters: "0019"
+ }
+
+ // UI tile definitions
+ tiles(scale: 2) {
+ multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
+ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
+ attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.light.on", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.light.off", backgroundColor:"#ffffff", nextState:"turningOn"
+ }
+ tileAttribute ("device.level", key: "SLIDER_CONTROL") {
+ attributeState "level", action:"switch level.setLevel"
+ }
+ tileAttribute ("colorName", key: "SECONDARY_CONTROL") {
+ attributeState "colorName", label:'${currentValue}'
+ }
+ }
+
+ standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2700..6500)") {
+ state "colorTemperature", action:"color temperature.setColorTemperature"
+ }
+ valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
+ state "colorTemperature", label: '${currentValue} K'
+ }
+
+ main(["switch"])
+ details(["switch", "colorTempSliderControl", "colorTemp", "refresh"])
+ }
+}
+
+// Parse incoming device messages to generate events
+def parse(String description) {
+ log.debug "description is $description"
+
+ def finalResult = zigbee.getKnownDescription(description)
+ if (finalResult) {
+ log.info finalResult
+ if (finalResult.type == "update") {
+ log.info "$device updates: ${finalResult.value}"
+ }
+ else {
+ sendEvent(name: finalResult.type, value: finalResult.value)
+ }
+ }
+ else {
+ log.warn "DID NOT PARSE MESSAGE for description : $description"
+ log.debug zigbee.parseDescriptionAsMap(description)
+ }
+}
+
+def off() {
+ zigbee.off()
+}
+
+def on() {
+ zigbee.on()
+}
+
+def setLevel(value) {
+ zigbee.setLevel(value)
+}
+
+def refresh() {
+ zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh() + zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig()
+}
+
+def configure() {
+ log.debug "Configuring Reporting and Bindings."
+ zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.colorTemperatureConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.colorTemperatureRefresh()
+}
+
+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)
+ }
+}
diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy
index f318ab7..5d6bf79 100644
--- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy
+++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy
@@ -143,7 +143,7 @@ def bulbDiscovery() {
if (numFound == 0)
app.updateSetting("selectedBulbs", "")
- if((bulbRefreshCount % 3) == 0) {
+ if((bulbRefreshCount % 5) == 0) {
discoverHueBulbs()
}
@@ -318,11 +318,15 @@ def addBulbs() {
def newHueBulb
if (bulbs instanceof java.util.Map) {
newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni }
- if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light")) {
- d = addChildDevice("smartthings", "Hue Lux Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
- } else {
- d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
- }
+ if (newHueBulb != null) {
+ if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light") ) {
+ d = addChildDevice("smartthings", "Hue Lux Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
+ } else {
+ d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
+ }
+ } else {
+ log.debug "$dni in not longer paired to the Hue Bridge or ID changed"
+ }
} else {
//backwards compatable
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni }
@@ -604,18 +608,16 @@ def parse(childDevice, description) {
}
}
-def on(childDevice, transition_deprecated = 0) {
+def on(childDevice) {
log.debug "Executing 'on'"
- def percent = childDevice.device?.currentValue("level") as Integer
- def level = Math.min(Math.round(percent * 255 / 100), 255)
- put("lights/${getId(childDevice)}/state", [bri: level, on: true])
- return "level: $percent"
+ put("lights/${getId(childDevice)}/state", [on: true])
+ return "Bulb is On"
}
-def off(childDevice, transition_deprecated = 0) {
+def off(childDevice) {
log.debug "Executing 'off'"
put("lights/${getId(childDevice)}/state", [on: false])
- return "level: 0"
+ return "Bulb is Off"
}
def setLevel(childDevice, percent) {
@@ -636,7 +638,7 @@ def setHue(childDevice, percent) {
put("lights/${getId(childDevice)}/state", [hue: level])
}
-def setColor(childDevice, huesettings, alert_deprecated = "", transition_deprecated = 0) {
+def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'"
def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
@@ -720,13 +722,8 @@ private getBridgeIP() {
host = d.latestState('networkAddress').stringValue
}
if (host == null || host == "") {
- def serialNumber = selectedHue
- def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value
- if (!bridge) {
- //failed because mac address sent from hub is wrong and doesn't match the hue's real mac address and serial number
- //in this case we will look up the bridge by comparing the incorrect mac addresses
- bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(serialNumber) }?.value
- }
+ def macAddress = selectedHue
+ def bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(macAddress) }?.value
if (bridge?.ip && bridge?.port) {
if (bridge?.ip.contains("."))
host = "${bridge?.ip}:${bridge?.port}"
diff --git a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
index 34f20b1..e82e5c7 100644
--- a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
+++ b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
@@ -61,10 +61,7 @@ def firstPage()
log.debug "REFRESH COUNT :: ${refreshCount}"
- if(!state.subscribe) {
- subscribe(location, null, locationHandler, [filterEvents:false])
- state.subscribe = true
- }
+ subscribe(location, null, locationHandler, [filterEvents:false])
//ssdp request every 25 seconds
if((refreshCount % 5) == 0) {
@@ -168,21 +165,30 @@ def getWemoLightSwitches()
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
-
- runIn(5, "subscribeToDevices") //initial subscriptions delayed by 5 seconds
- runIn(10, "refreshDevices") //refresh devices, delayed by 10 seconds
- runIn(900, "doDeviceSync" , [overwrite: false]) //setup ip:port syncing every 15 minutes
-
- // SUBSCRIBE responses come back with TIMEOUT-1801 (30 minutes), so we refresh things a bit before they expire (29 minutes)
- runIn(1740, "refresh", [overwrite: false])
}
def updated() {
log.debug "Updated with settings: ${settings}"
initialize()
+}
- runIn(5, "subscribeToDevices") //subscribe again to new/old devices wait 5 seconds
- runIn(10, "refreshDevices") //refresh devices again, delayed by 10 seconds
+def initialize() {
+ unsubscribe()
+ unschedule()
+ subscribe(location, null, locationHandler, [filterEvents:false])
+
+ if (selectedSwitches)
+ addSwitches()
+
+ if (selectedMotions)
+ addMotions()
+
+ if (selectedLightSwitches)
+ addLightSwitches()
+
+ runIn(5, "subscribeToDevices") //initial subscriptions delayed by 5 seconds
+ runIn(10, "refreshDevices") //refresh devices, delayed by 10 seconds
+ runEvery5Minutes("refresh")
}
def resubscribe() {
@@ -192,8 +198,7 @@ def resubscribe() {
def refresh() {
log.debug "refresh() called"
- //reschedule the refreshes
- runIn(1740, "refresh", [overwrite: false])
+ doDeviceSync()
refreshDevices()
}
@@ -236,7 +241,8 @@ def addSwitches() {
"port": selectedSwitch.value.port
]
])
-
+ def ipvalue = convertHexToIP(selectedSwitch.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
@@ -266,8 +272,9 @@ def addMotions() {
"port": selectedMotion.value.port
]
])
-
- log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
+ def ipvalue = convertHexToIP(selectedMotion.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
+ log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
}
@@ -296,7 +303,8 @@ def addLightSwitches() {
"port": selectedLightSwitch.value.port
]
])
-
+ def ipvalue = convertHexToIP(selectedLightSwitch.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
log.debug "created ${d.displayName} with id $dni"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
@@ -304,27 +312,6 @@ def addLightSwitches() {
}
}
-def initialize() {
- // remove location subscription afterwards
- unsubscribe()
- state.subscribe = false
-
- if (selectedSwitches)
- {
- addSwitches()
- }
-
- if (selectedMotions)
- {
- addMotions()
- }
-
- if (selectedLightSwitches)
- {
- addLightSwitches()
- }
-}
-
def locationHandler(evt) {
def description = evt.description
def hub = evt?.hubId
@@ -333,53 +320,32 @@ def locationHandler(evt) {
log.debug parsedEvent
if (parsedEvent?.ssdpTerm?.contains("Belkin:device:controllee") || parsedEvent?.ssdpTerm?.contains("Belkin:device:insight")) {
-
def switches = getWemoSwitches()
-
- if (!(switches."${parsedEvent.ssdpUSN.toString()}"))
- { //if it doesn't already exist
+ if (!(switches."${parsedEvent.ssdpUSN.toString()}")) {
+ //if it doesn't already exist
switches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else {
log.debug "Device was already found in state..."
-
def d = switches."${parsedEvent.ssdpUSN.toString()}"
boolean deviceChangedValues = false
-
+ log.debug "$d.ip <==> $parsedEvent.ip"
if(d.ip != parsedEvent.ip || d.port != parsedEvent.port) {
d.ip = parsedEvent.ip
d.port = parsedEvent.port
deviceChangedValues = true
log.debug "Device's port or ip changed..."
+ def child = getChildDevice(parsedEvent.mac)
+ child.subscribe(parsedEvent.ip, parsedEvent.port)
+ child.poll()
}
-
- if (deviceChangedValues) {
- def children = getChildDevices()
- log.debug "Found children ${children}"
- children.each {
- if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
- log.debug "updating ip and port, and resubscribing, for device ${it} with mac ${parsedEvent.mac}"
- it.subscribe(parsedEvent.ip, parsedEvent.port)
- }
- }
- }
-
}
-
}
else if (parsedEvent?.ssdpTerm?.contains("Belkin:device:sensor")) {
-
def motions = getWemoMotions()
-
- if (!(motions."${parsedEvent.ssdpUSN.toString()}"))
- { //if it doesn't already exist
+ if (!(motions."${parsedEvent.ssdpUSN.toString()}")) {
+ //if it doesn't already exist
motions << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else { // just update the values
log.debug "Device was already found in state..."
def d = motions."${parsedEvent.ssdpUSN.toString()}"
@@ -412,10 +378,7 @@ def locationHandler(evt) {
if (!(lightSwitches."${parsedEvent.ssdpUSN.toString()}"))
{ //if it doesn't already exist
lightSwitches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else {
log.debug "Device was already found in state..."
def d = lightSwitches."${parsedEvent.ssdpUSN.toString()}"
@@ -426,21 +389,11 @@ def locationHandler(evt) {
d.port = parsedEvent.port
deviceChangedValues = true
log.debug "Device's port or ip changed..."
+ def child = getChildDevice(parsedEvent.mac)
+ log.debug "updating ip and port, and resubscribing, for device with mac ${parsedEvent.mac}"
+ child.subscribe(parsedEvent.ip, parsedEvent.port)
}
-
- if (deviceChangedValues) {
- def children = getChildDevices()
- log.debug "Found children ${children}"
- children.each {
- if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
- log.debug "updating ip and port, and resubscribing, for device ${it} with mac ${parsedEvent.mac}"
- it.subscribe(parsedEvent.ip, parsedEvent.port)
- }
- }
- }
-
}
-
}
else if (parsedEvent.headers && parsedEvent.body) {
String headerString = new String(parsedEvent.headers.decodeBase64())?.toLowerCase()
@@ -580,73 +533,30 @@ private def parseDiscoveryMessage(String description) {
}
}
}
-
device
}
def doDeviceSync(){
log.debug "Doing Device Sync!"
- runIn(900, "doDeviceSync" , [overwrite: false]) //schedule to run again in 15 minutes
-
- if(!state.subscribe) {
- subscribe(location, null, locationHandler, [filterEvents:false])
- state.subscribe = true
- }
-
discoverAllWemoTypes()
}
-def pollChildren() {
- def devices = getAllChildDevices()
- devices.each { d ->
- //only poll switches?
- d.poll()
- }
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
-def delayPoll() {
- log.debug "Executing 'delayPoll'"
-
- runIn(5, "pollChildren")
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
}
-/*def poll() {
- log.debug "Executing 'poll'"
- runIn(600, "poll", [overwrite: false]) //schedule to run again in 10 minutes
-
- def lastPoll = getLastPollTime()
- def currentTime = now()
- def lastPollDiff = currentTime - lastPoll
- log.debug "lastPoll: $lastPoll, currentTime: $currentTime, lastPollDiff: $lastPollDiff"
- setLastPollTime(currentTime)
-
- doDeviceSync()
-}
-
-
-def setLastPollTime(currentTime) {
- state.lastpoll = currentTime
-}
-
-def getLastPollTime() {
- state.lastpoll ?: now()
-}
-
-def now() {
- new Date().getTime()
-}*/
-
-private Boolean canInstallLabs()
-{
+private Boolean canInstallLabs() {
return hasAllHubsOver("000.011.00603")
}
-private Boolean hasAllHubsOver(String desiredFirmware)
-{
+private Boolean hasAllHubsOver(String desiredFirmware) {
return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
}
-private List getRealHubFirmwareVersions()
-{
+private List getRealHubFirmwareVersions() {
return location.hubs*.firmwareVersionString.findAll { it }
}
diff --git a/smartapps/tslagle13/routine-director.src/routine-director.groovy b/smartapps/tslagle13/routine-director.src/routine-director.groovy
index 86263b9..21d0c48 100644
--- a/smartapps/tslagle13/routine-director.src/routine-director.groovy
+++ b/smartapps/tslagle13/routine-director.src/routine-director.groovy
@@ -50,7 +50,7 @@ preferences {
}
section("Send Notifications?") {
input("recipients", "contact", title: "Send notifications to") {
- input "phone", "phone", title: "Send an SMS to this number?"
+ input "phone", "phone", title: "Send an SMS to this number?", required:false
}
}
@@ -266,7 +266,9 @@ def sendAway(msg) {
}
else {
sendPush(msg)
- sendSms(phone, msg)
+ if(phone){
+ sendSms(phone, msg)
+ }
}
}
@@ -280,7 +282,9 @@ def sendHome(msg) {
}
else {
sendPush(msg)
- sendSms(phone, msg)
+ if(phone){
+ sendSms(phone, msg)
+ }
}
}
@@ -339,4 +343,4 @@ private getTimeIntervalLabel() {
private hideOptionsSection() {
(starting || ending || days || modes) ? false: true
-}
\ No newline at end of file
+}