From 96f2c5ed8bbb29a810a0610db968d76b2b9ac2a7 Mon Sep 17 00:00:00 2001 From: Daniel Date: Mon, 23 Nov 2015 11:38:25 -0600 Subject: [PATCH 1/3] MSA-699: Vinli Home Connect allows users to control their Smartthings Devices with their Vinli connect vehicles. The Vinli device is an OBD dongle that can report when it leaves or enters geofences. A user can, for instance, set their doors to lock and lights to turn off when they leave proximity to their home. --- .../vinli-home-connect.groovy | 196 ++++++++++++++++++ 1 file changed, 196 insertions(+) create mode 100644 smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy diff --git a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy new file mode 100644 index 0000000..be213ad --- /dev/null +++ b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy @@ -0,0 +1,196 @@ +/** + * Vinli Home Beta + * + * Copyright 2015 Daniel + * + */ +definition( + name: "Vinli Home Connect", + namespace: "com.vinli.smartthings", + author: "Daniel", + description: "Allows Vinli users to connect their car to Smart Things", + category: "SmartThings Labs", + iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", + iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + oauth: true) + +preferences { + section ("Allow external service to control these things...") { + input "switches", "capability.switch", multiple: true, required: true + input "locks", "capability.lock", multiple: true, required: true + } +} + +mappings { + + path("/devices") { + action: [ + GET: "listAllDevices" + ] + } + + path("/switches") { + action: [ + GET: "listSwitches" + ] + } + path("/switches/:command") { + action: [ + PUT: "updateSwitches" + ] + } + path("/switches/:id/:command") { + action: [ + PUT: "updateSwitch" + ] + } + path("/locks/:command") { + action: [ + PUT: "updateLocks" + ] + } + path("/locks/:id/:command") { + action: [ + PUT: "updateLock" + ] + } + + path("/devices/:id/:command") { + action: [ + PUT: "commandDevice" + ] + } +} + +// returns a list of all devices +def listAllDevices() { + def resp = [] + switches.each { + resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub.name] + } + + locks.each { + resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub.name] + } + return resp +} + +// returns a list like +// [[name: "kitchen lamp", value: "off"], [name: "bathroom", value: "on"]] +def listSwitches() { + def resp = [] + switches.each { + resp << [name: it.displayName, value: it.currentValue("switch"), type: "switch", id: it.id] + } + return resp +} + +void updateLocks() { + // use the built-in request object to get the command parameter + def command = params.command + + if (command) { + + // check that the switch supports the specified command + // If not, return an error using httpError, providing a HTTP status code. + locks.each { + if (!it.hasCommand(command)) { + httpError(501, "$command is not a valid command for all switches specified") + } + } + + // all switches have the comand + // execute the command on all switches + // (note we can do this on the array - the command will be invoked on every element + locks."$command"() + } +} + +void updateLock() { + def command = params.command + + locks.each { + if (!it.hasCommand(command)) { + httpError(400, "$command is not a valid command for all lock specified") + } + + if (it.id == params.id) { + it."$command"() + } + } +} + +void updateSwitch() { + def command = params.command + + switches.each { + if (!it.hasCommand(command)) { + httpError(400, "$command is not a valid command for all switches specified") + } + + if (it.id == params.id) { + it."$command"() + } + } +} + +void commandDevice() { + def command = params.command + def devices = [] + + switches.each { + devices << it + } + + locks.each { + devices << it + } + + devices.each { + if (it.id == params.id) { + if (!it.hasCommand(command)) { + httpError(400, "$command is not a valid command for specified device") + } + it."$command"() + } + } +} + +void updateSwitches() { + // use the built-in request object to get the command parameter + def command = params.command + + if (command) { + + // check that the switch supports the specified command + // If not, return an error using httpError, providing a HTTP status code. + switches.each { + if (!it.hasCommand(command)) { + httpError(400, "$command is not a valid command for all switches specified") + } + } + + // all switches have the comand + // execute the command on all switches + // (note we can do this on the array - the command will be invoked on every element + switches."$command"() + } +} + + def installed() { + log.debug "Installed with settings: ${settings}" + + initialize() +} + +def updated() { + log.debug "Updated with settings: ${settings}" + + unsubscribe() + initialize() +} + +def initialize() { + // TODO: subscribe to attributes, devices, locations, etc.s +} \ No newline at end of file From 21041570dbb5f679f471c6a01d4d36a35efe8539 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Dec 2015 09:40:00 -0600 Subject: [PATCH 2/3] Modifying 'Vinli Home Connect' --- .../vinli-home-connect.src/vinli-home-connect.groovy | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy index be213ad..525ce6e 100644 --- a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy +++ b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy @@ -8,7 +8,7 @@ definition( name: "Vinli Home Connect", namespace: "com.vinli.smartthings", author: "Daniel", - description: "Allows Vinli users to connect their car to Smart Things", + description: "Allows Vinli users to connect their car to SmartThings", category: "SmartThings Labs", iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", @@ -180,17 +180,10 @@ void updateSwitches() { def installed() { log.debug "Installed with settings: ${settings}" - - initialize() } def updated() { log.debug "Updated with settings: ${settings}" unsubscribe() - initialize() } - -def initialize() { - // TODO: subscribe to attributes, devices, locations, etc.s -} \ No newline at end of file From 3ea70fecad8982a5d9a956bffdfecd7d706dbec3 Mon Sep 17 00:00:00 2001 From: umangparekh Date: Mon, 21 Dec 2015 15:49:52 -0800 Subject: [PATCH 3/3] Fine-tuning the Motion Threshold and Motion Threshold multiplier for manufacturer Smarthings. Garage Door use case works + implemented code review feedback Implementing code review feedback + inverted the signedY to match Centralite like behavior. Implementing code review feedback + inverted the signedY to match Centralite like behavior. --- .../smartsense-multi-sensor.groovy | 79 +++++++++++++++---- 1 file changed, 62 insertions(+), 17 deletions(-) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index ae18c1c..4cdece9 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -371,21 +371,50 @@ def getTemperature(value) { def refresh() { log.debug "Refreshing Values " - def refreshCmds = [ - /* sensitivity - default value (8) */ + def refreshCmds = [] - "zcl mfg-code ${manufacturerCode}", "delay 200", - "zcl global write 0xFC02 0 0x20 {02}", "delay 200", - "send 0x${device.deviceNetworkId} 1 1", "delay 400", + if (device.getDataValue("manufacturer") == "SmartThings") { + + log.debug "Refreshing Values for manufacturer: SmartThings " + refreshCmds = refreshCmds + [ - "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", - "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200", + /* These values of Motion Threshold Multiplier(01) and Motion Threshold (D200) + seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer. + Separating these out in a separate if-else because I do not want to touch Centralite part + as of now. + */ - "zcl mfg-code ${manufacturerCode}", "delay 200", - "zcl global read 0xFC02 0x0010", - "send 0x${device.deviceNetworkId} 1 1","delay 400" - ] + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global write 0xFC02 0 0x20 {01}", "delay 200", + "send 0x${device.deviceNetworkId} 1 1", "delay 400", + + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global write 0xFC02 2 0x21 {D200}", "delay 200", + "send 0x${device.deviceNetworkId} 1 1", "delay 400", + + ] + + + } else { + refreshCmds = refreshCmds + [ + + /* sensitivity - default value (8) */ + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global write 0xFC02 0 0x20 {02}", "delay 200", + "send 0x${device.deviceNetworkId} 1 1", "delay 400", + ] + } + + //Common refresh commands + refreshCmds = refreshCmds + [ + "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", + "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200", + + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global read 0xFC02 0x0010", + "send 0x${device.deviceNetworkId} 1 1","delay 400" + ] return refreshCmds + enrollResponse() } @@ -461,19 +490,34 @@ private Map parseAxis(String description) { xyzResults.x = signedX log.debug "X Part: ${signedX}" } + // Y and the Z axes are interchanged between SmartThings's implementation and Centralite's implementation else if (part.startsWith("13")) { def unsignedY = hexToInt(part.split("13")[1].trim()) def signedY = unsignedY > 32767 ? unsignedY - 65536 : unsignedY - xyzResults.y = signedY - log.debug "Y Part: ${signedY}" + if (device.getDataValue("manufacturer") == "SmartThings") { + xyzResults.z = -signedY + log.debug "Z Part: ${xyzResults.z}" + if (garageSensor == "Yes") + garageEvent(xyzResults.z) + } + else { + xyzResults.y = signedY + log.debug "Y Part: ${signedY}" + } } else if (part.startsWith("14")) { def unsignedZ = hexToInt(part.split("14")[1].trim()) def signedZ = unsignedZ > 32767 ? unsignedZ - 65536 : unsignedZ - xyzResults.z = signedZ - log.debug "Z Part: ${signedZ}" - if (garageSensor == "Yes") - garageEvent(signedZ) + if (device.getDataValue("manufacturer") == "SmartThings") { + xyzResults.y = signedZ + log.debug "Y Part: ${signedZ}" + } else { + xyzResults.z = signedZ + log.debug "Z Part: ${signedZ}" + if (garageSensor == "Yes") + garageEvent(signedZ) + + } } } @@ -553,3 +597,4 @@ private byte[] reverseArray(byte[] array) { return array } +