diff --git a/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy b/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
index b5f9f5c..1bd78f3 100644
--- a/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
+++ b/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy
@@ -25,6 +25,8 @@ metadata {
capability "Refresh"
capability "Sensor"
+ attribute "currentIP", "string"
+
command "subscribe"
command "resubscribe"
command "unsubscribe"
@@ -34,21 +36,36 @@ metadata {
// simulator metadata
simulator {}
- // UI tile definitions
- tiles {
- standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
- state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
- state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
- state "turningOn", label:'${name}', icon:"st.switches.switch.on", backgroundColor:"#79b821"
- state "turningOff", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ffffff"
- }
- standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
- }
+ // UI tile definitions
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
+ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
+ attributeState "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
- main "switch"
- details (["switch", "refresh"])
- }
+ standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
+ state "on", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ state "off", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "turningOn", label:'${name}', action:"switch.off", icon:"st.Home.home30", backgroundColor:"#79b821", nextState:"turningOff"
+ state "turningOff", label:'${name}', action:"switch.on", icon:"st.Home.home30", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "offline", label:'${name}', icon:"st.Home.home30", backgroundColor:"#ff0000"
+ }
+
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ main(["switch"])
+ details(["rich-control", "refresh"])
+ }
}
// parse events into attributes
@@ -68,6 +85,7 @@ def parse(String description) {
def result = []
def bodyString = msg.body
if (bodyString) {
+ unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) {
@@ -78,13 +96,14 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "Notify: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}")
} else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
} else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
log.trace "GetBinaryResponse: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
+ def dispaux = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux)
}
}
@@ -101,14 +120,6 @@ private getCallBackAddress() {
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
-private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
-}
-
-private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
-}
-
private getHostAddress() {
def ip = getDataValue("ip")
def port = getDataValue("port")
@@ -195,6 +206,8 @@ def subscribe(ip, port) {
if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip"
updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
}
if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port"
@@ -259,6 +272,8 @@ User-Agent: CyberGarage-HTTP/1.0
def poll() {
log.debug "Executing 'poll'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
@@ -274,3 +289,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+
+def setOffline() {
+ sendEvent(name: "switch", value: "offline", descriptionText: "The device is offline")
+}
+
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
+}
+
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+}
diff --git a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
index eb3ea10..9649db4 100644
--- a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
+++ b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy
@@ -21,6 +21,8 @@
capability "Refresh"
capability "Sensor"
+ attribute "currentIP", "string"
+
command "subscribe"
command "resubscribe"
command "unsubscribe"
@@ -31,17 +33,30 @@
}
// UI tile definitions
- tiles {
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "motion", canChangeIcon: true){
+ tileAttribute ("device.motion", key: "PRIMARY_CONTROL") {
+ attributeState "active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0"
+ attributeState "inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff"
+ attributeState "offline", label:'${name}', icon:"st.motion.motion.active", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
+
standardTile("motion", "device.motion", width: 2, height: 2) {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
- }
- standardTile("refresh", "device.motion", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
+ state("offline", label:'${name}', icon:"st.motion.motion.inactive", backgroundColor:"#ff0000")
}
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
main "motion"
- details (["motion", "refresh"])
+ details (["rich-control", "refresh"])
}
}
@@ -62,6 +77,7 @@ def parse(String description) {
def result = []
def bodyString = msg.body
if (bodyString) {
+ unschedule("setOffline")
def body = new XmlSlurper().parseText(bodyString)
if (body?.property?.TimeSyncRequest?.text()) {
@@ -72,7 +88,7 @@ def parse(String description) {
} else if (body?.property?.BinaryState?.text()) {
def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "active" : "inactive"
log.debug "Notify - BinaryState = ${value}"
- result << createEvent(name: "motion", value: value)
+ result << createEvent(name: "motion", value: value, descriptionText: "Motion is ${value}")
} else if (body?.property?.TimeZoneNotification?.text()) {
log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
}
@@ -91,14 +107,6 @@ private getCallBackAddress() {
device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
-private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
-}
-
-private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
-}
-
private getHostAddress() {
def ip = getDataValue("ip")
def port = getDataValue("port")
@@ -125,6 +133,8 @@ def refresh() {
////////////////////////////
def getStatus() {
log.debug "Executing WeMo Motion 'getStatus'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
@@ -165,7 +175,9 @@ def subscribe(ip, port) {
def existingPort = getDataValue("port")
if (ip && ip != existingIp) {
log.debug "Updating ip from $existingIp to $ip"
- updateDataValue("ip", ip)
+ updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
}
if (port && port != existingPort) {
log.debug "Updating port from $existingPort to $port"
@@ -226,3 +238,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+
+def setOffline() {
+ sendEvent(name: "motion", value: "offline", descriptionText: "The device is offline")
+}
+
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
+}
+
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+}
diff --git a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
index b385ceb..cd9e0ec 100644
--- a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
+++ b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy
@@ -10,120 +10,142 @@
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
- * Wemo Switch
+ * Wemo Switch
*
- * Author: superuser
- * Date: 2013-10-11
+ * Author: Juan Risso (SmartThings)
+ * Date: 2015-10-11
*/
metadata {
- definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") {
- capability "Actuator"
- capability "Switch"
- capability "Polling"
- capability "Refresh"
- capability "Sensor"
+ definition (name: "Wemo Switch", namespace: "smartthings", author: "SmartThings") {
+ capability "Actuator"
+ capability "Switch"
+ capability "Polling"
+ capability "Refresh"
+ capability "Sensor"
- command "subscribe"
- command "resubscribe"
- command "unsubscribe"
- }
+ attribute "currentIP", "string"
- // simulator metadata
- simulator {}
+ command "subscribe"
+ command "resubscribe"
+ command "unsubscribe"
+ }
- // UI tile definitions
- tiles {
- standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
- state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821"
- state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff"
- }
- standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat") {
- state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
- }
+ // simulator metadata
+ simulator {}
- main "switch"
- details (["switch", "refresh"])
- }
+ // UI tile definitions
+ tiles(scale: 2) {
+ multiAttributeTile(name:"rich-control", type: "switch", canChangeIcon: true){
+ tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
+ attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ attributeState "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
+ }
+ tileAttribute ("currentIP", key: "SECONDARY_CONTROL") {
+ attributeState "currentIP", label: ''
+ }
+ }
+
+ standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
+ state "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ state "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.off", backgroundColor:"#79b821", nextState:"turningOff"
+ state "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
+ state "offline", label:'${name}', icon:"st.switches.switch.off", backgroundColor:"#ff0000"
+ }
+
+ standardTile("refresh", "device.switch", inactiveLabel: false, height: 2, width: 2, decoration: "flat") {
+ state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
+ }
+
+ main(["switch"])
+ details(["rich-control", "refresh"])
+ }
}
// parse events into attributes
def parse(String description) {
- log.debug "Parsing '${description}'"
+ log.debug "Parsing '${description}'"
- def msg = parseLanMessage(description)
- def headerString = msg.header
+ def msg = parseLanMessage(description)
+ def headerString = msg.header
- if (headerString?.contains("SID: uuid:")) {
- def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
- sid -= "SID: uuid:".trim()
+ if (headerString?.contains("SID: uuid:")) {
+ def sid = (headerString =~ /SID: uuid:.*/) ? ( headerString =~ /SID: uuid:.*/)[0] : "0"
+ sid -= "SID: uuid:".trim()
- updateDataValue("subscriptionId", sid)
- }
+ updateDataValue("subscriptionId", sid)
+ }
- def result = []
- def bodyString = msg.body
- if (bodyString) {
- def body = new XmlSlurper().parseText(bodyString)
-
- if (body?.property?.TimeSyncRequest?.text()) {
- log.trace "Got TimeSyncRequest"
- result << timeSyncResponse()
- } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
- log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
- } else if (body?.property?.BinaryState?.text()) {
- def value = body?.property?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
- log.trace "Notify: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
- } else if (body?.property?.TimeZoneNotification?.text()) {
- log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
- } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
- def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().toInteger() == 1 ? "on" : "off"
- log.trace "GetBinaryResponse: BinaryState = ${value}"
- result << createEvent(name: "switch", value: value)
- }
- }
-
- result
+ def result = []
+ def bodyString = msg.body
+ if (bodyString) {
+ unschedule("setOffline")
+ def body = new XmlSlurper().parseText(bodyString)
+ if (body?.property?.TimeSyncRequest?.text()) {
+ log.trace "Got TimeSyncRequest"
+ result << timeSyncResponse()
+ } else if (body?.Body?.SetBinaryStateResponse?.BinaryState?.text()) {
+ log.trace "Got SetBinaryStateResponse = ${body?.Body?.SetBinaryStateResponse?.BinaryState?.text()}"
+ } else if (body?.property?.BinaryState?.text()) {
+ def value = body?.property?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
+ log.trace "Notify: BinaryState = ${value}, ${body.property.BinaryState}"
+ def dispaux = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux)
+ } else if (body?.property?.TimeZoneNotification?.text()) {
+ log.debug "Notify: TimeZoneNotification = ${body?.property?.TimeZoneNotification?.text()}"
+ } else if (body?.Body?.GetBinaryStateResponse?.BinaryState?.text()) {
+ def value = body?.Body?.GetBinaryStateResponse?.BinaryState?.text().substring(0, 1).toInteger() == 0 ? "off" : "on"
+ log.trace "GetBinaryResponse: BinaryState = ${value}, ${body.property.BinaryState}"
+ log.info "Connection: ${device.currentValue("connection")}"
+ if (device.currentValue("currentIP") == "Offline") {
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "IP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
+ }
+ def dispaux2 = device.currentValue("switch") != value
+ result << createEvent(name: "switch", value: value, descriptionText: "Switch is ${value}", displayed: dispaux2)
+ }
+ }
+ result
}
private getTime() {
- // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
- ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
+ // This is essentially System.currentTimeMillis()/1000, but System is disallowed by the sandbox.
+ ((new GregorianCalendar().time.time / 1000l).toInteger()).toString()
}
private getCallBackAddress() {
- device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
+ device.hub.getDataValue("localIP") + ":" + device.hub.getDataValue("localSrvPortTCP")
}
private Integer convertHexToInt(hex) {
- Integer.parseInt(hex,16)
+ Integer.parseInt(hex,16)
}
private String convertHexToIP(hex) {
- [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
private getHostAddress() {
- def ip = getDataValue("ip")
- def port = getDataValue("port")
-
- if (!ip || !port) {
- def parts = device.deviceNetworkId.split(":")
- if (parts.length == 2) {
- ip = parts[0]
- port = parts[1]
- } else {
- log.warn "Can't figure out ip and port for device: ${device.id}"
- }
- }
- log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
- return convertHexToIP(ip) + ":" + convertHexToInt(port)
+ def ip = getDataValue("ip")
+ def port = getDataValue("port")
+ if (!ip || !port) {
+ def parts = device.deviceNetworkId.split(":")
+ if (parts.length == 2) {
+ ip = parts[0]
+ port = parts[1]
+ } else {
+ log.warn "Can't figure out ip and port for device: ${device.id}"
+ }
+ }
+ log.debug "Using ip: ${ip} and port: ${port} for device: ${device.id}"
+ return convertHexToIP(ip) + ":" + convertHexToInt(port)
}
-
def on() {
- log.debug "Executing 'on'"
- sendEvent(name: "switch", value: "on")
+log.debug "Executing 'on'"
def turnOn = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()}
@@ -133,17 +155,16 @@ Content-Length: 333
-
+
1
-
+
""", physicalgraph.device.Protocol.LAN)
}
def off() {
- log.debug "Executing 'off'"
- sendEvent(name: "switch", value: "off")
- def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
+log.debug "Executing 'off'"
+def turnOff = new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPAction: "urn:Belkin:service:basicevent:1#SetBinaryState"
Host: ${getHostAddress()}
Content-Type: text/xml
@@ -152,36 +173,13 @@ Content-Length: 333
-
+
0
-
+
""", physicalgraph.device.Protocol.LAN)
}
-/*def refresh() {
- log.debug "Executing 'refresh'"
-new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
-SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
-Content-Length: 277
-Content-Type: text/xml; charset="utf-8"
-HOST: ${getHostAddress()}
-User-Agent: CyberGarage-HTTP/1.0
-
-
-
-
-
-
-
-""", physicalgraph.device.Protocol.LAN)
-}*/
-
-def refresh() {
- log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
- [subscribe(), timeSyncResponse(), poll()]
-}
-
def subscribe(hostAddress) {
log.debug "Executing 'subscribe()'"
def address = getCallBackAddress()
@@ -200,27 +198,30 @@ def subscribe() {
subscribe(getHostAddress())
}
-def subscribe(ip, port) {
- def existingIp = getDataValue("ip")
- def existingPort = getDataValue("port")
- if (ip && ip != existingIp) {
- log.debug "Updating ip from $existingIp to $ip"
- updateDataValue("ip", ip)
- }
- if (port && port != existingPort) {
- log.debug "Updating port from $existingPort to $port"
- updateDataValue("port", port)
- }
+def refresh() {
+ log.debug "Executing WeMo Switch 'subscribe', then 'timeSyncResponse', then 'poll'"
+ [subscribe(), timeSyncResponse(), poll()]
+}
+def subscribe(ip, port) {
+ def existingIp = getDataValue("ip")
+ def existingPort = getDataValue("port")
+ if (ip && ip != existingIp) {
+ log.debug "Updating ip from $existingIp to $ip"
+ updateDataValue("ip", ip)
+ def ipvalue = convertHexToIP(getDataValue("ip"))
+ sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP changed to ${ipvalue}")
+ }
+ if (port && port != existingPort) {
+ log.debug "Updating port from $existingPort to $port"
+ updateDataValue("port", port)
+ }
subscribe("${ip}:${port}")
}
-////////////////////////////
def resubscribe() {
-log.debug "Executing 'resubscribe()'"
-
-def sid = getDeviceDataByName("subscriptionId")
-
+ log.debug "Executing 'resubscribe()'"
+ def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""SUBSCRIBE /upnp/event/basicevent1 HTTP/1.1
HOST: ${getHostAddress()}
SID: uuid:${sid}
@@ -228,12 +229,11 @@ TIMEOUT: Second-5400
""", physicalgraph.device.Protocol.LAN)
-
}
-////////////////////////////
+
def unsubscribe() {
-def sid = getDeviceDataByName("subscriptionId")
+ def sid = getDeviceDataByName("subscriptionId")
new physicalgraph.device.HubAction("""UNSUBSCRIBE publisher path HTTP/1.1
HOST: ${getHostAddress()}
SID: uuid:${sid}
@@ -242,7 +242,7 @@ SID: uuid:${sid}
""", physicalgraph.device.Protocol.LAN)
}
-////////////////////////////
+
//TODO: Use UTC Timezone
def timeSyncResponse() {
log.debug "Executing 'timeSyncResponse()'"
@@ -267,9 +267,15 @@ User-Agent: CyberGarage-HTTP/1.0
""", physicalgraph.device.Protocol.LAN)
}
+def setOffline() {
+ //sendEvent(name: "currentIP", value: "Offline", displayed: false)
+ sendEvent(name: "switch", value: "offline", descriptionText: "The device is offline")
+}
def poll() {
log.debug "Executing 'poll'"
+if (device.currentValue("currentIP") != "Offline")
+ runIn(10, setOffline)
new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1
SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState"
Content-Length: 277
diff --git a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
index 34f20b1..e82e5c7 100644
--- a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
+++ b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy
@@ -61,10 +61,7 @@ def firstPage()
log.debug "REFRESH COUNT :: ${refreshCount}"
- if(!state.subscribe) {
- subscribe(location, null, locationHandler, [filterEvents:false])
- state.subscribe = true
- }
+ subscribe(location, null, locationHandler, [filterEvents:false])
//ssdp request every 25 seconds
if((refreshCount % 5) == 0) {
@@ -168,21 +165,30 @@ def getWemoLightSwitches()
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
-
- runIn(5, "subscribeToDevices") //initial subscriptions delayed by 5 seconds
- runIn(10, "refreshDevices") //refresh devices, delayed by 10 seconds
- runIn(900, "doDeviceSync" , [overwrite: false]) //setup ip:port syncing every 15 minutes
-
- // SUBSCRIBE responses come back with TIMEOUT-1801 (30 minutes), so we refresh things a bit before they expire (29 minutes)
- runIn(1740, "refresh", [overwrite: false])
}
def updated() {
log.debug "Updated with settings: ${settings}"
initialize()
+}
- runIn(5, "subscribeToDevices") //subscribe again to new/old devices wait 5 seconds
- runIn(10, "refreshDevices") //refresh devices again, delayed by 10 seconds
+def initialize() {
+ unsubscribe()
+ unschedule()
+ subscribe(location, null, locationHandler, [filterEvents:false])
+
+ if (selectedSwitches)
+ addSwitches()
+
+ if (selectedMotions)
+ addMotions()
+
+ if (selectedLightSwitches)
+ addLightSwitches()
+
+ runIn(5, "subscribeToDevices") //initial subscriptions delayed by 5 seconds
+ runIn(10, "refreshDevices") //refresh devices, delayed by 10 seconds
+ runEvery5Minutes("refresh")
}
def resubscribe() {
@@ -192,8 +198,7 @@ def resubscribe() {
def refresh() {
log.debug "refresh() called"
- //reschedule the refreshes
- runIn(1740, "refresh", [overwrite: false])
+ doDeviceSync()
refreshDevices()
}
@@ -236,7 +241,8 @@ def addSwitches() {
"port": selectedSwitch.value.port
]
])
-
+ def ipvalue = convertHexToIP(selectedSwitch.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
@@ -266,8 +272,9 @@ def addMotions() {
"port": selectedMotion.value.port
]
])
-
- log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
+ def ipvalue = convertHexToIP(selectedMotion.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
+ log.debug "Created ${d.displayName} with id: ${d.id}, dni: ${d.deviceNetworkId}"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
}
@@ -296,7 +303,8 @@ def addLightSwitches() {
"port": selectedLightSwitch.value.port
]
])
-
+ def ipvalue = convertHexToIP(selectedLightSwitch.value.ip)
+ d.sendEvent(name: "currentIP", value: ipvalue, descriptionText: "IP is ${ipvalue}")
log.debug "created ${d.displayName} with id $dni"
} else {
log.debug "found ${d.displayName} with id $dni already exists"
@@ -304,27 +312,6 @@ def addLightSwitches() {
}
}
-def initialize() {
- // remove location subscription afterwards
- unsubscribe()
- state.subscribe = false
-
- if (selectedSwitches)
- {
- addSwitches()
- }
-
- if (selectedMotions)
- {
- addMotions()
- }
-
- if (selectedLightSwitches)
- {
- addLightSwitches()
- }
-}
-
def locationHandler(evt) {
def description = evt.description
def hub = evt?.hubId
@@ -333,53 +320,32 @@ def locationHandler(evt) {
log.debug parsedEvent
if (parsedEvent?.ssdpTerm?.contains("Belkin:device:controllee") || parsedEvent?.ssdpTerm?.contains("Belkin:device:insight")) {
-
def switches = getWemoSwitches()
-
- if (!(switches."${parsedEvent.ssdpUSN.toString()}"))
- { //if it doesn't already exist
+ if (!(switches."${parsedEvent.ssdpUSN.toString()}")) {
+ //if it doesn't already exist
switches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else {
log.debug "Device was already found in state..."
-
def d = switches."${parsedEvent.ssdpUSN.toString()}"
boolean deviceChangedValues = false
-
+ log.debug "$d.ip <==> $parsedEvent.ip"
if(d.ip != parsedEvent.ip || d.port != parsedEvent.port) {
d.ip = parsedEvent.ip
d.port = parsedEvent.port
deviceChangedValues = true
log.debug "Device's port or ip changed..."
+ def child = getChildDevice(parsedEvent.mac)
+ child.subscribe(parsedEvent.ip, parsedEvent.port)
+ child.poll()
}
-
- if (deviceChangedValues) {
- def children = getChildDevices()
- log.debug "Found children ${children}"
- children.each {
- if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
- log.debug "updating ip and port, and resubscribing, for device ${it} with mac ${parsedEvent.mac}"
- it.subscribe(parsedEvent.ip, parsedEvent.port)
- }
- }
- }
-
}
-
}
else if (parsedEvent?.ssdpTerm?.contains("Belkin:device:sensor")) {
-
def motions = getWemoMotions()
-
- if (!(motions."${parsedEvent.ssdpUSN.toString()}"))
- { //if it doesn't already exist
+ if (!(motions."${parsedEvent.ssdpUSN.toString()}")) {
+ //if it doesn't already exist
motions << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else { // just update the values
log.debug "Device was already found in state..."
def d = motions."${parsedEvent.ssdpUSN.toString()}"
@@ -412,10 +378,7 @@ def locationHandler(evt) {
if (!(lightSwitches."${parsedEvent.ssdpUSN.toString()}"))
{ //if it doesn't already exist
lightSwitches << ["${parsedEvent.ssdpUSN.toString()}":parsedEvent]
- }
- else
- { // just update the values
-
+ } else {
log.debug "Device was already found in state..."
def d = lightSwitches."${parsedEvent.ssdpUSN.toString()}"
@@ -426,21 +389,11 @@ def locationHandler(evt) {
d.port = parsedEvent.port
deviceChangedValues = true
log.debug "Device's port or ip changed..."
+ def child = getChildDevice(parsedEvent.mac)
+ log.debug "updating ip and port, and resubscribing, for device with mac ${parsedEvent.mac}"
+ child.subscribe(parsedEvent.ip, parsedEvent.port)
}
-
- if (deviceChangedValues) {
- def children = getChildDevices()
- log.debug "Found children ${children}"
- children.each {
- if (it.getDeviceDataByName("mac") == parsedEvent.mac) {
- log.debug "updating ip and port, and resubscribing, for device ${it} with mac ${parsedEvent.mac}"
- it.subscribe(parsedEvent.ip, parsedEvent.port)
- }
- }
- }
-
}
-
}
else if (parsedEvent.headers && parsedEvent.body) {
String headerString = new String(parsedEvent.headers.decodeBase64())?.toLowerCase()
@@ -580,73 +533,30 @@ private def parseDiscoveryMessage(String description) {
}
}
}
-
device
}
def doDeviceSync(){
log.debug "Doing Device Sync!"
- runIn(900, "doDeviceSync" , [overwrite: false]) //schedule to run again in 15 minutes
-
- if(!state.subscribe) {
- subscribe(location, null, locationHandler, [filterEvents:false])
- state.subscribe = true
- }
-
discoverAllWemoTypes()
}
-def pollChildren() {
- def devices = getAllChildDevices()
- devices.each { d ->
- //only poll switches?
- d.poll()
- }
+private String convertHexToIP(hex) {
+ [convertHexToInt(hex[0..1]),convertHexToInt(hex[2..3]),convertHexToInt(hex[4..5]),convertHexToInt(hex[6..7])].join(".")
}
-def delayPoll() {
- log.debug "Executing 'delayPoll'"
-
- runIn(5, "pollChildren")
+private Integer convertHexToInt(hex) {
+ Integer.parseInt(hex,16)
}
-/*def poll() {
- log.debug "Executing 'poll'"
- runIn(600, "poll", [overwrite: false]) //schedule to run again in 10 minutes
-
- def lastPoll = getLastPollTime()
- def currentTime = now()
- def lastPollDiff = currentTime - lastPoll
- log.debug "lastPoll: $lastPoll, currentTime: $currentTime, lastPollDiff: $lastPollDiff"
- setLastPollTime(currentTime)
-
- doDeviceSync()
-}
-
-
-def setLastPollTime(currentTime) {
- state.lastpoll = currentTime
-}
-
-def getLastPollTime() {
- state.lastpoll ?: now()
-}
-
-def now() {
- new Date().getTime()
-}*/
-
-private Boolean canInstallLabs()
-{
+private Boolean canInstallLabs() {
return hasAllHubsOver("000.011.00603")
}
-private Boolean hasAllHubsOver(String desiredFirmware)
-{
+private Boolean hasAllHubsOver(String desiredFirmware) {
return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
}
-private List getRealHubFirmwareVersions()
-{
+private List getRealHubFirmwareVersions() {
return location.hubs*.firmwareVersionString.findAll { it }
}