Merge pull request #240 from SmartThingsCommunity/master

Master -> Staging
This commit is contained in:
Juan Pablo Risso
2015-10-31 00:21:46 -04:00
9 changed files with 699 additions and 473 deletions

View File

@@ -24,6 +24,8 @@ metadata {
capability "Battery" capability "Battery"
attribute "tamper", "enum", ["detected", "clear"] 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" 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: " 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) { tiles(scale: 2) {
multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){ multiAttributeTile(name:"motion", type: "generic", width: 6, height: 4){
tileAttribute ("device.motion", key: "PRIMARY_CONTROL") { tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
@@ -85,53 +100,78 @@ metadata {
valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) { valueTile("humidity", "device.humidity", inactiveLabel: false, width: 2, height: 2) {
state "humidity", label:'${currentValue}% humidity', unit:"" state "humidity", label:'${currentValue}% humidity', unit:""
} }
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) { 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) { valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:"" state "battery", label:'${currentValue}% battery', unit:""
} }
main(["motion", "temperature", "humidity", "illuminance"]) valueTile("batteryStatus", "device.batteryStatus", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
details(["motion", "temperature", "humidity", "illuminance", "battery"]) 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() def updated() {
{ log.debug "Updated with settings: ${settings}"
if (state.sec && !isConfigured()) { log.debug "${device.displayName} is now ${device.latestValue("powerSupply")}"
// in case we miss the SCSR
if (device.latestValue("powerSupply") == "USB Cable") { //case1: USB powered
response(configure()) 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 def result = null
if (description.startsWith("Err 106")) { if (description.startsWith("Err 106")) {
state.sec = 0 log.debug "parse() >> Err 106"
result = createEvent( name: "secureInclusion", value: "failed", isStateChange: true, 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") { } else if (description != "updated") {
log.debug "parse() >> zwave.parse(description)"
def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1]) def cmd = zwave.parse(description, [0x31: 5, 0x30: 2, 0x84: 1])
if (cmd) { if (cmd) {
result = zwaveEvent(cmd) result = zwaveEvent(cmd)
} }
} }
log.debug "Parsed '${description}' to ${result.inspect()}" log.debug "After zwaveEvent(cmd) >> Parsed '${description}' to ${result.inspect()}"
return result 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 result = [createEvent(descriptionText: "${device.displayName} woke up", isStateChange: false)]
def cmds = []
if (!isConfigured()) { if (!isConfigured()) {
// we're still in the process of configuring a newly joined device
log.debug("late configure") log.debug("late configure")
result += response(configure()) result << response(configure())
} else { } else {
result += response(zwave.wakeUpV1.wakeUpNoMoreInformation()) log.debug("Device has been configured sending >> wakeUpNoMoreInformation()")
cmds << zwave.wakeUpV1.wakeUpNoMoreInformation().format()
result << response(cmds)
} }
result result
} }
@@ -149,10 +189,29 @@ def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulat
} }
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityCommandsSupportedReport cmd) { 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 zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def result = []
def map = [ name: "battery", unit: "%" ] def map = [ name: "battery", unit: "%" ]
if (cmd.batteryLevel == 0xFF) { if (cmd.batteryLevel == 0xFF) {
map.value = 1 map.value = 1
@@ -162,11 +221,14 @@ def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
map.value = cmd.batteryLevel map.value = cmd.batteryLevel
} }
state.lastbatt = now() 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 = [:] def map = [:]
switch (cmd.sensorType) { switch (cmd.sensorType) {
case 1: case 1:
@@ -208,7 +270,6 @@ def motionEvent(value) {
} }
def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) { def zwaveEvent(physicalgraph.zwave.commands.sensorbinaryv2.SensorBinaryReport cmd) {
setConfigured()
motionEvent(cmd.sensorValue) motionEvent(cmd.sensorValue)
} }
@@ -225,47 +286,112 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
result << createEvent(name: "tamper", value: "clear", displayed: false) result << createEvent(name: "tamper", value: "clear", displayed: false)
break break
case 3: 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 break
case 7: case 7:
result << motionEvent(1) result << motionEvent(1)
break break
} }
} else { } else {
log.warn "Need to handle this cmd.notificationType: ${cmd.notificationType}"
result << createEvent(descriptionText: cmd.toString(), isStateChange: false) result << createEvent(descriptionText: cmd.toString(), isStateChange: false)
} }
result 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) { def zwaveEvent(physicalgraph.zwave.Command cmd) {
log.debug "General zwaveEvent cmd: ${cmd}"
createEvent(descriptionText: cmd.toString(), isStateChange: false) createEvent(descriptionText: cmd.toString(), isStateChange: false)
} }
def configure() { def configure() {
// This sensor joins as a secure device if you double-click the button to include it // 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 "${device.displayName} is configuring its settings"
log.debug "Multi 6 not sending configure until secure" def request = []
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),
// report every 8 minutes (threshold reports don't work on battery power) //1. set association groups for hub
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 8*60), request << zwave.associationV1.associationSet(groupingIdentifier:1, nodeId:zwaveHubNodeId)
// report automatically on threshold change request << zwave.associationV1.associationSet(groupingIdentifier:2, nodeId:zwaveHubNodeId)
zwave.configurationV1.configurationSet(parameterNumber: 40, size: 1, scaledConfigurationValue: 1),
//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()] commands(request) + ["delay 20000", zwave.wakeUpV1.wakeUpNoMoreInformation().format()]
} }
private setConfigured() { private def getTimeOptionValueMap() { [
updateDataValue("configured", "true") "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() { private isConfigured() {
@@ -281,5 +407,6 @@ private command(physicalgraph.zwave.Command cmd) {
} }
private commands(commands, delay=200) { private commands(commands, delay=200) {
log.info "sending commands: ${commands}"
delayBetween(commands.collect{ command(it) }, delay) delayBetween(commands.collect{ command(it) }, delay)
} }

View File

@@ -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 off", action:"indicator.indicatorWhenOn", icon:"st.indicators.lit-when-off"
state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on" state "when on", action:"indicator.indicatorNever", icon:"st.indicators.lit-when-on"
state "never", action:"indicator.indicatorWhenOff", icon:"st.indicators.never-lit" 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"]) main(["switch"])
details(["switch", "refresh", "indicator"]) details(["switch", "level", "indicator", "refresh"])
} }
} }
def parse(String description) { def parse(String description) {
def item1 = [ def result = null
canBeCurrentState: false, if (description != "updated") {
linkText: getLinkText(device), log.debug "parse() >> zwave.parse($description)"
isStateChange: false, def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
displayed: false, if (cmd) {
descriptionText: description, result = zwaveEvent(cmd)
value: description }
]
def result
def cmd = zwave.parse(description, [0x20: 1, 0x26: 1, 0x70: 1])
if (cmd) {
result = createEvent(cmd, item1)
} }
else { if (result?.name == 'hail' && hubFirmwareLessThan("000.011.00602")) {
item1.displayed = displayed(description, item1.isStateChange) result = [result, response(zwave.basicV1.basicGet())]
result = [item1] log.debug "Was hailed: requesting state update"
} else {
log.debug "Parse returned ${result?.descriptionText}"
} }
log.debug "Parse returned ${result?.descriptionText}" return result
result
} }
def createEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd, Map item1) { def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd) {
def result = doCreateEvent(cmd, item1) dimmerEvents(cmd)
for (int i = 0; i < result.size(); i++) { }
result[i].type = "physical"
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) { def zwaveEvent(physicalgraph.zwave.commands.configurationv1.ConfigurationReport cmd) {
log.debug "ConfigurationReport $cmd"
def value = "when off" def value = "when off"
if (cmd.configurationValue[0] == 1) {value = "when on"} if (cmd.configurationValue[0] == 1) {value = "when on"}
if (cmd.configurationValue[0] == 2) {value = "never"} 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) { def zwaveEvent(physicalgraph.zwave.commands.hailv1.Hail cmd) {
// Handles any Z-Wave commands we aren't interested in createEvent([name: "hail", value: "hail", descriptionText: "Switch button was pressed", displayed: false])
log.debug "UNHANDLED COMMAND $cmd" }
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() { def on() {
log.info "on" delayBetween([
delayBetween([zwave.basicV1.basicSet(value: 0xFF).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000) zwave.basicV1.basicSet(value: 0xFF).format(),
zwave.switchMultilevelV1.switchMultilevelGet().format()
],5000)
} }
def off() { 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) { def setLevel(value) {
log.debug "setLevel >> value: $value"
def valueaux = value as Integer 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) delayBetween ([zwave.basicV1.basicSet(value: level).format(), zwave.switchMultilevelV1.switchMultilevelGet().format()], 5000)
} }
def setLevel(value, duration) { def setLevel(value, duration) {
log.debug "setLevel >> value: $value, duration: $duration"
def valueaux = value as Integer 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) 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() { def poll() {
@@ -197,21 +192,27 @@ def poll() {
} }
def refresh() { 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() { 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() zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()
} }
def indicatorWhenOff() { 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() zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()
} }
def indicatorNever() { def indicatorNever() {
sendEvent(name: "indicatorStatus", value: "never", display: false) sendEvent(name: "indicatorStatus", value: "never")
zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format() zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()
} }
@@ -222,4 +223,4 @@ def invertSwitch(invert=true) {
else { else {
zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format() zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 4, size: 1).format()
} }
} }

View File

@@ -25,6 +25,8 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
attribute "currentIP", "string"
command "subscribe" command "subscribe"
command "resubscribe" command "resubscribe"
command "unsubscribe" command "unsubscribe"
@@ -34,21 +36,36 @@ metadata {
// simulator metadata // simulator metadata
simulator {} simulator {}
// UI tile definitions // UI tile definitions
tiles { tiles(scale: 2) {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) { multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff" tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn" attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821" attributeState "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff" 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"
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") { attributeState "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh" }
} tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
attributeState "currentIP", label: ''
}
}
main "switch" standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
details (["switch", "refresh"]) 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 // parse events into attributes
@@ -68,6 +85,7 @@ def parse(String description) {
def result = [] def result = []
def bodyString = msg.body def bodyString = msg.body
if (bodyString) { if (bodyString) {
unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString) def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) { if (body?.property?.TimeSyncRequest?.text()) {
@@ -78,13 +96,14 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) { } else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off" def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "Notify: BinaryState = ${value}" 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()) { } else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}" log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
} else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) { } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off" def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "GetBinaryResponse: BinaryState = ${value}" 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") 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() { private getHostAddress() {
def ip = getDataValue("ip") def ip = getDataValue("ip")
def port = getDataValue("port") def port = getDataValue("port")
@@ -195,6 +206,8 @@ def subscribe(ip, port) {
if (ip && ip != existingIp) { if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip" 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) { if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port" log.debug "Updating port from $existingPort to $port"
@@ -259,6 +272,8 @@ User-Agent: CyberGarage-HTTP/1.0
def poll() { def poll() {
log.debug "Executing 'poll'" log.debug "Executing 'poll'"
if (device.currentValue("currentIP") != "Offline")
runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277 Content-Length: 277
@@ -274,3 +289,15 @@ User-Agent: CyberGarage-HTTP/1.0
</s:Body> </s:Body>
</s:Envelope>""", physicalgraph.device.Protocol.LAN) </s:Envelope>""", 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(".")
}

View File

@@ -21,6 +21,8 @@
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
attribute "currentIP", "string"
command "subscribe" command "subscribe"
command "resubscribe" command "resubscribe"
command "unsubscribe" command "unsubscribe"
@@ -31,17 +33,30 @@
} }
// UI tile definitions // 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) { standardTile("motion", "device.motion", width: 2, height: 2) {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0") state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff") state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
} state("offline", label:'${name}', icon:"st.motion.motion.inactive", backgroundColor:"#ff0000")
standardTile("refresh", "device.motion", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
} }
standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "motion" main "motion"
details (["motion", "refresh"]) details (["rich-control", "refresh"])
} }
} }
@@ -62,6 +77,7 @@ def parse(String description) {
def result = [] def result = []
def bodyString = msg.body def bodyString = msg.body
if (bodyString) { if (bodyString) {
unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString) def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) { if (body?.property?.TimeSyncRequest?.text()) {
@@ -72,7 +88,7 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) { } else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "active" : "inactive" def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "active" : "inactive"
log.debug "Notify - BinaryState = ${value}" 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()) { } else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${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") 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() { private getHostAddress() {
def ip = getDataValue("ip") def ip = getDataValue("ip")
def port = getDataValue("port") def port = getDataValue("port")
@@ -125,6 +133,8 @@ def refresh() {
//////////////////////////// ////////////////////////////
def getStatus() { def getStatus() {
log.debug "Executing WeMo Motion '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 new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277 Content-Length: 277
@@ -165,7 +175,9 @@ def subscribe(ip, port) {
def existingPort = getDataValue("port") def existingPort = getDataValue("port")
if (ip && ip != existingIp) { if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip" 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) { if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port" log.debug "Updating port from $existingPort to $port"
@@ -226,3 +238,15 @@ User-Agent: CyberGarage-HTTP/1.0
</s:Envelope> </s:Envelope>
""", physicalgraph.device.Protocol.LAN) """, 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(".")
}

View File

@@ -10,120 +10,142 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License * 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. * for the specific language governing permissions and limitations under the License.
* *
* Wemo Switch * Wemo Switch
* *
* Author: superuser * Author: Juan Risso (SmartThings)
* Date: 2013-10-11 * Date: 2015-10-11
*/ */
metadata { metadata {
definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") { definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") {
capability "Actuator" capability "Actuator"
capability "Switch" capability "Switch"
capability "Polling" capability "Polling"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
command "subscribe" attribute "currentIP", "string"
command "resubscribe"
command "unsubscribe"
}
// simulator metadata command "subscribe"
simulator {} command "resubscribe"
command "unsubscribe"
}
// UI tile definitions // simulator metadata
tiles { simulator {}
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"
}
main "switch" // UI tile definitions
details (["switch", "refresh"]) 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 // parse events into attributes
def parse(String description) { def parse(String description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
def msg = parseLanMessage(description) def msg = parseLanMessage(description)
def headerString = msg.header def headerString = msg.header
if (headerString?.contains("SID: uuid:")) { if (headerString?.contains("SID: uuid:")) {
def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0" def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
sid -= "SID: uuid:".trim() sid -= "SID: uuid:".trim()
updateDataValue("subscriptionId", sid) updateDataValue("subscriptionId", sid)
} }
def result = [] def result = []
def bodyString = msg.body def bodyString = msg.body
if (bodyString) { if (bodyString) {
def body = new XmlSlurper().parseText(bodyString) unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) { if (body?.property?.TimeSyncRequest?.text()) {
log.trace "Got TimeSyncRequest" log.trace "Got TimeSyncRequest"
result << timeSyncResponse() result << timeSyncResponse()
} else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) { } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}" log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
} else if (body?.property?.BinaryState?.text()) { } else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off" def value = body?.property?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
log.trace "Notify: BinaryState = ${value}" log.trace "Notify: BinaryState = ${value}, ${body.property.BinaryState}"
result << createEvent(name: "switch", value: value) def dispaux = device.currentValue("switch") != value
} else if (body?.property?.TimeZoneNotification?.text()) { result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux)
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}" } else if (body?.property?.TimeZoneNotification?.text()) {
} else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) { log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off" } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
log.trace "GetBinaryResponse: BinaryState = ${value}" def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
result << createEvent(name: "switch", value: value) log.trace "GetBinaryResponse: BinaryState = ${value}, ${body.property.BinaryState}"
} log.info "Connection: ${device.currentValue("connection")}"
} if (device.currentValue("currentIP") == "Offline") {
def ipvalue = convertHexToIP(getDataValue("ip"))
result 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() { private getTime() {
// This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox. // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
((new GregorianCalendar().time.time / 1000l).toInteger()).toString() ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
} }
private getCallBackAddress() { private getCallBackAddress() {
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP") device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
} }
private Integer convertHexToInt(hex) { private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16) Integer.parseInt(hex,16)
} }
private String convertHexToIP(hex) { 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() { private getHostAddress() {
def ip = getDataValue("ip") def ip = getDataValue("ip")
def port = getDataValue("port") def port = getDataValue("port")
if (!ip || !port) {
if (!ip || !port) { def parts = device.deviceNetworkId.split(":")
def parts = device.deviceNetworkId.split(":") if (parts.length == 2) {
if (parts.length == 2) { ip = parts[0]
ip = parts[0] port = parts[1]
port = parts[1] } else {
} else { log.warn "Can't figure out ip and port for device: ${device.id}"
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}"
log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}" return convertHexToIP(ip) + ":" + convertHexToInt(port)
return convertHexToIP(ip) + ":" + convertHexToInt(port)
} }
def on() { def on() {
log.debug "Executing 'on'" log.debug "Executing 'on'"
sendEvent(name: "switch", value: "on")
def turnOn = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 def turnOn = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState" SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()} Host: ${getHostAddress()}
@@ -133,17 +155,16 @@ Content-Length: 333
<?xml version="1.0"?> <?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body> <SOAP-ENV:Body>
<m:SetBinaryState xmlns:m="urn:Belkin:service:basicevent:1"> <m:SetBinaryState xmlns:m="urn:Belkin:service:basicevent:1">
<BinaryState>1</BinaryState> <BinaryState>1</BinaryState>
</m:SetBinaryState> </m:SetBinaryState>
</SOAP-ENV:Body> </SOAP-ENV:Body>
</SOAP-ENV:Envelope>""", physicalgraph.device.Protocol.LAN) </SOAP-ENV:Envelope>""", physicalgraph.device.Protocol.LAN)
} }
def off() { def off() {
log.debug "Executing 'off'" log.debug "Executing 'off'"
sendEvent(name: "switch", value: "off") def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState" SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()} Host: ${getHostAddress()}
Content-Type: text/xml Content-Type: text/xml
@@ -152,36 +173,13 @@ Content-Length: 333
<?xml version="1.0"?> <?xml version="1.0"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"> <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" SOAP-ENV:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<SOAP-ENV:Body> <SOAP-ENV:Body>
<m:SetBinaryState xmlns:m="urn:Belkin:service:basicevent:1"> <m:SetBinaryState xmlns:m="urn:Belkin:service:basicevent:1">
<BinaryState>0</BinaryState> <BinaryState>0</BinaryState>
</m:SetBinaryState> </m:SetBinaryState>
</SOAP-ENV:Body> </SOAP-ENV:Body>
</SOAP-ENV:Envelope>""", physicalgraph.device.Protocol.LAN) </SOAP-ENV:Envelope>""", 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
<?xml version="1.0" encoding="utf-8"?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<s:Body>
<u:GetBinaryState xmlns:u="urn:Belkin:service:basicevent:1">
</u:GetBinaryState>
</s:Body>
</s:Envelope>""", physicalgraph.device.Protocol.LAN)
}*/
def refresh() {
log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
[subscribe(), timeSyncResponse(), poll()]
}
def subscribe(hostAddress) { def subscribe(hostAddress) {
log.debug "Executing 'subscribe()'" log.debug "Executing 'subscribe()'"
def address = getCallBackAddress() def address = getCallBackAddress()
@@ -200,27 +198,30 @@ def subscribe() {
subscribe(getHostAddress()) subscribe(getHostAddress())
} }
def subscribe(ip, port) { def refresh() {
def existingIp = getDataValue("ip") log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
def existingPort = getDataValue("port") [subscribe(), timeSyncResponse(), poll()]
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 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}") subscribe("${ip}:${port}")
} }
////////////////////////////
def resubscribe() { def resubscribe() {
log.debug "Executing 'resubscribe()'" log.debug "Executing 'resubscribe()'"
def sid = getDeviceDataByName("subscriptionId")
def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1 new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1
HOST: ${getHostAddress()} HOST: ${getHostAddress()}
SID: uuid:${sid} SID: uuid:${sid}
@@ -228,12 +229,11 @@ TIMEOUT: Second-5400
""", physicalgraph.device.Protocol.LAN) """, physicalgraph.device.Protocol.LAN)
} }
////////////////////////////
def unsubscribe() { def unsubscribe() {
def sid = getDeviceDataByName("subscriptionId") def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""UNSUBSCRIBE publisher path HTTP/1.1 new physicalgraph.device.HubAction("""UNSUBSCRIBE publisher path HTTP/1.1
HOST: ${getHostAddress()} HOST: ${getHostAddress()}
SID: uuid:${sid} SID: uuid:${sid}
@@ -242,7 +242,7 @@ SID: uuid:${sid}
""", physicalgraph.device.Protocol.LAN) """, physicalgraph.device.Protocol.LAN)
} }
////////////////////////////
//TODO: Use UTC Timezone //TODO: Use UTC Timezone
def timeSyncResponse() { def timeSyncResponse() {
log.debug "Executing 'timeSyncResponse()'" log.debug "Executing 'timeSyncResponse()'"
@@ -267,9 +267,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN) """, 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() { def poll() {
log.debug "Executing 'poll'" log.debug "Executing 'poll'"
if (device.currentValue("currentIP") != "Offline")
runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277 Content-Length: 277

View File

@@ -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)
}
}

View File

@@ -143,7 +143,7 @@ def bulbDiscovery() {
if (numFound == 0) if (numFound == 0)
app.updateSetting("selectedBulbs", "") app.updateSetting("selectedBulbs", "")
if((bulbRefreshCount % 3) == 0) { if((bulbRefreshCount % 5) == 0) {
discoverHueBulbs() discoverHueBulbs()
} }
@@ -318,11 +318,15 @@ def addBulbs() {
def newHueBulb def newHueBulb
if (bulbs instanceof java.util.Map) { if (bulbs instanceof java.util.Map) {
newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni } newHueBulb = bulbs.find { (app.id + "/" + it.value.id) == dni }
if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light")) { if (newHueBulb != null) {
d = addChildDevice("smartthings", "Hue Lux Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name]) if (newHueBulb?.value?.type?.equalsIgnoreCase("Dimmable light") ) {
} else { d = addChildDevice("smartthings", "Hue Lux Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name])
d = addChildDevice("smartthings", "Hue 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 { } else {
//backwards compatable //backwards compatable
newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni } 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'" log.debug "Executing 'on'"
def percent = childDevice.device?.currentValue("level") as Integer put("lights/${getId(childDevice)}/state", [on: true])
def level = Math.min(Math.round(percent * 255 / 100), 255) return "Bulb is On"
put("lights/${getId(childDevice)}/state", [bri: level, on: true])
return "level: $percent"
} }
def off(childDevice, transition_deprecated = 0) { def off(childDevice) {
log.debug "Executing 'off'" log.debug "Executing 'off'"
put("lights/${getId(childDevice)}/state", [on: false]) put("lights/${getId(childDevice)}/state", [on: false])
return "level: 0" return "Bulb is Off"
} }
def setLevel(childDevice, percent) { def setLevel(childDevice, percent) {
@@ -636,7 +638,7 @@ def setHue(childDevice, percent) {
put("lights/${getId(childDevice)}/state", [hue: level]) 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)'" log.debug "Executing 'setColor($huesettings)'"
def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535) def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255) def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
@@ -720,13 +722,8 @@ private getBridgeIP() {
host = d.latestState('networkAddress').stringValue host = d.latestState('networkAddress').stringValue
} }
if (host == null || host == "") { if (host == null || host == "") {
def serialNumber = selectedHue def macAddress = selectedHue
def bridge = getHueBridges().find { it?.value?.serialNumber?.equalsIgnoreCase(serialNumber) }?.value def bridge = getHueBridges().find { it?.value?.mac?.equalsIgnoreCase(macAddress) }?.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
}
if (bridge?.ip && bridge?.port) { if (bridge?.ip && bridge?.port) {
if (bridge?.ip.contains(".")) if (bridge?.ip.contains("."))
host = "${bridge?.ip}:${bridge?.port}" host = "${bridge?.ip}:${bridge?.port}"

View File

@@ -61,10 +61,7 @@ def firstPage()
log.debug "REFRESH COUNT :: ${refreshCount}" log.debug "REFRESH COUNT :: ${refreshCount}"
if(!state.subscribe) { subscribe(location, null, locationHandler, [filterEvents:false])
subscribe(location, null, locationHandler, [filterEvents:false])
state.subscribe = true
}
//ssdp request every 25 seconds //ssdp request every 25 seconds
if((refreshCount % 5) == 0) { if((refreshCount % 5) == 0) {
@@ -168,21 +165,30 @@ def getWemoLightSwitches()
def installed() { def installed() {
log.debug "Installed with settings: ${settings}" log.debug "Installed with settings: ${settings}"
initialize() 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() { def updated() {
log.debug "Updated with settings: ${settings}" log.debug "Updated with settings: ${settings}"
initialize() initialize()
}
runIn(5, "subscribeToDevices") //subscribe again to new/old devices wait 5 seconds def initialize() {
runIn(10, "refreshDevices") //refresh devices again, delayed by 10 seconds 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() { def resubscribe() {
@@ -192,8 +198,7 @@ def resubscribe() {
def refresh() { def refresh() {
log.debug "refresh() called" log.debug "refresh() called"
//reschedule the refreshes doDeviceSync()
runIn(1740, "refresh", [overwrite: false])
refreshDevices() refreshDevices()
} }
@@ -236,7 +241,8 @@ def addSwitches() {
"port": selectedSwitch.value.port "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}" log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else { } else {
log.debug "found ${d.displayName} with id $dni already exists" log.debug "found ${d.displayName} with id $dni already exists"
@@ -266,8 +272,9 @@ def addMotions() {
"port": selectedMotion.value.port "port": selectedMotion.value.port
] ]
]) ])
def ipvalue = convertHexToIP(selectedMotion.value.ip)
log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}" d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else { } else {
log.debug "found ${d.displayName} with id $dni already exists" log.debug "found ${d.displayName} with id $dni already exists"
} }
@@ -296,7 +303,8 @@ def addLightSwitches() {
"port": selectedLightSwitch.value.port "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" log.debug "created ${d.displayName} with id $dni"
} else { } else {
log.debug "found ${d.displayName} with id $dni already exists" 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 locationHandler(evt) {
def description = evt.description def description = evt.description
def hub = evt?.hubId def hub = evt?.hubId
@@ -333,53 +320,32 @@ def locationHandler(evt) {
log.debug parsedEvent log.debug parsedEvent
if (parsedEvent?.ssdpTerm?.contains("Belkin:device:controllee") || parsedEvent?.ssdpTerm?.contains("Belkin:device:insight")) { if (parsedEvent?.ssdpTerm?.contains("Belkin:device:controllee") || parsedEvent?.ssdpTerm?.contains("Belkin:device:insight")) {
def switches = getWemoSwitches() def switches = getWemoSwitches()
if (!(switches."${parsedEvent.ssdpUSN.toString()}")) {
if (!(switches."${parsedEvent.ssdpUSN.toString()}")) //if it doesn't already exist
{ //if it doesn't already exist
switches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent] switches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
} } else {
else
{ // just update the values
log.debug "Device was already found in state..." log.debug "Device was already found in state..."
def d = switches."${parsedEvent.ssdpUSN.toString()}" def d = switches."${parsedEvent.ssdpUSN.toString()}"
boolean deviceChangedValues = false boolean deviceChangedValues = false
log.debug "$d.ip <==> $parsedEvent.ip"
if(d.ip != parsedEvent.ip || d.port != parsedEvent.port) { if(d.ip != parsedEvent.ip || d.port != parsedEvent.port) {
d.ip = parsedEvent.ip d.ip = parsedEvent.ip
d.port = parsedEvent.port d.port = parsedEvent.port
deviceChangedValues = true deviceChangedValues = true
log.debug "Device's port or ip changed..." 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")) { else if (parsedEvent?.ssdpTerm?.contains("Belkin:device:sensor")) {
def motions = getWemoMotions() def motions = getWemoMotions()
if (!(motions."${parsedEvent.ssdpUSN.toString()}")) {
if (!(motions."${parsedEvent.ssdpUSN.toString()}")) //if it doesn't already exist
{ //if it doesn't already exist
motions << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent] motions << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
} } else { // just update the values
else
{ // just update the values
log.debug "Device was already found in state..." log.debug "Device was already found in state..."
def d = motions."${parsedEvent.ssdpUSN.toString()}" def d = motions."${parsedEvent.ssdpUSN.toString()}"
@@ -412,10 +378,7 @@ def locationHandler(evt) {
if (!(lightSwitches."${parsedEvent.ssdpUSN.toString()}")) if (!(lightSwitches."${parsedEvent.ssdpUSN.toString()}"))
{ //if it doesn't already exist { //if it doesn't already exist
lightSwitches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent] lightSwitches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
} } else {
else
{ // just update the values
log.debug "Device was already found in state..." log.debug "Device was already found in state..."
def d = lightSwitches."${parsedEvent.ssdpUSN.toString()}" def d = lightSwitches."${parsedEvent.ssdpUSN.toString()}"
@@ -426,21 +389,11 @@ def locationHandler(evt) {
d.port = parsedEvent.port d.port = parsedEvent.port
deviceChangedValues = true deviceChangedValues = true
log.debug "Device's port or ip changed..." 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) { else if (parsedEvent.headers && parsedEvent.body) {
String headerString = new String(parsedEvent.headers.decodeBase64())?.toLowerCase() String headerString = new String(parsedEvent.headers.decodeBase64())?.toLowerCase()
@@ -580,73 +533,30 @@ private def parseDiscoveryMessage(String description) {
} }
} }
} }
device device
} }
def doDeviceSync(){ def doDeviceSync(){
log.debug "Doing Device Sync!" 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() discoverAllWemoTypes()
} }
def pollChildren() { private String convertHexToIP(hex) {
def devices = getAllChildDevices() [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
devices.each { d ->
//only poll switches?
d.poll()
}
} }
def delayPoll() { private Integer convertHexToInt(hex) {
log.debug "Executing 'delayPoll'" Integer.parseInt(hex,16)
runIn(5, "pollChildren")
} }
/*def poll() { private Boolean canInstallLabs() {
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()
{
return hasAllHubsOver("000.011.00603") return hasAllHubsOver("000.011.00603")
} }
private Boolean hasAllHubsOver(String desiredFirmware) private Boolean hasAllHubsOver(String desiredFirmware) {
{
return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware } return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
} }
private List getRealHubFirmwareVersions() private List getRealHubFirmwareVersions() {
{
return location.hubs*.firmwareVersionString.findAll { it } return location.hubs*.firmwareVersionString.findAll { it }
} }

View File

@@ -50,7 +50,7 @@ preferences {
} }
section("Send Notifications?") { section("Send Notifications?") {
input("recipients", "contact", title: "Send notifications to") { 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 { else {
sendPush(msg) sendPush(msg)
sendSms(phone, msg) if(phone){
sendSms(phone, msg)
}
} }
} }
@@ -280,7 +282,9 @@ def sendHome(msg) {
} }
else { else {
sendPush(msg) sendPush(msg)
sendSms(phone, msg) if(phone){
sendSms(phone, msg)
}
} }
} }
@@ -339,4 +343,4 @@ private getTimeIntervalLabel() {
private hideOptionsSection() { private hideOptionsSection() {
(starting || ending || days || modes) ? false: true (starting || ending || days || modes) ? false: true
} }