mirror of
https://github.com/mtan93/SmartThingsPublic.git
synced 2026-03-25 13:04:10 +00:00
Merge pull request #240 from SmartThingsCommunity/master
Master -> Staging
This commit is contained in:
@@ -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)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -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(".")
|
||||||
|
}
|
||||||
|
|||||||
@@ -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(".")
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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}"
|
||||||
|
|||||||
@@ -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 }
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user