From 358cf261e8aca3bebf0141973e73180e763e42f4 Mon Sep 17 00:00:00 2001 From: Matt Pennig Date: Tue, 22 Dec 2015 11:14:53 -0600 Subject: [PATCH 1/8] Adding multiAttributeTile definition to Simulated Thermostat device type handler --- .../simulated-thermostat.groovy | 63 ++++++++++++++----- 1 file changed, 47 insertions(+), 16 deletions(-) diff --git a/devicetypes/smartthings/testing/simulated-thermostat.src/simulated-thermostat.groovy b/devicetypes/smartthings/testing/simulated-thermostat.src/simulated-thermostat.groovy index 5010ed8..ed0b23c 100644 --- a/devicetypes/smartthings/testing/simulated-thermostat.src/simulated-thermostat.groovy +++ b/devicetypes/smartthings/testing/simulated-thermostat.src/simulated-thermostat.groovy @@ -1,5 +1,5 @@ /** - * Copyright 2014 SmartThings + * 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: @@ -15,6 +15,7 @@ metadata { // Automatically generated. Make future change here. definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") { capability "Thermostat" + capability "Relative Humidity Measurement" command "tempUp" command "tempDown" @@ -22,11 +23,40 @@ metadata { command "heatDown" command "coolUp" command "coolDown" - command "setTemperature", ["number"] + command "setTemperature", ["number"] } - tiles { - valueTile("temperature", "device.temperature", width: 1, height: 1) { + tiles(scale: 2) { + multiAttributeTile(name:"thermostatMulti", type:"thermostat", width:6, height:4) { + tileAttribute("device.temperature", key: "PRIMARY_CONTROL") { + attributeState("default", label:'${currentValue}', unit:"dF") + } + tileAttribute("device.temperature", key: "VALUE_CONTROL") { + attributeState("default", action: "setTemperature") + } + tileAttribute("device.humidity", key: "SECONDARY_CONTROL") { + attributeState("default", label:'${currentValue}%', unit:"%") + } + tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") { + attributeState("idle", backgroundColor:"#44b621") + attributeState("heating", backgroundColor:"#ffa81e") + attributeState("cooling", backgroundColor:"#269bd2") + } + tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") { + attributeState("off", label:'${name}') + attributeState("heat", label:'${name}') + attributeState("cool", label:'${name}') + attributeState("auto", label:'${name}') + } + tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") { + attributeState("default", label:'${currentValue}', unit:"dF") + } + tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") { + attributeState("default", label:'${currentValue}', unit:"dF") + } + } + + valueTile("temperature", "device.temperature", width: 2, height: 2) { state("temperature", label:'${currentValue}', unit:"dF", backgroundColors:[ [value: 31, color: "#153591"], @@ -39,51 +69,51 @@ metadata { ] ) } - standardTile("tempDown", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'down', action:"tempDown" } - standardTile("tempUp", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'up', action:"tempUp" } - valueTile("heatingSetpoint", "device.heatingSetpoint", inactiveLabel: false, decoration: "flat") { + valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff" } - standardTile("heatDown", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'down', action:"heatDown" } - standardTile("heatUp", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'up', action:"heatUp" } - valueTile("coolingSetpoint", "device.coolingSetpoint", inactiveLabel: false, decoration: "flat") { + valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff" } - standardTile("coolDown", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'down', action:"coolDown" } - standardTile("coolUp", "device.temperature", inactiveLabel: false, decoration: "flat") { + standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "default", label:'up', action:"coolUp" } - standardTile("mode", "device.thermostatMode", inactiveLabel: false, decoration: "flat") { + standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "off", label:'${name}', action:"thermostat.heat", backgroundColor:"#ffffff" state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffa81e" state "cool", label:'${name}', action:"thermostat.auto", backgroundColor:"#269bd2" state "auto", label:'${name}', action:"thermostat.off", backgroundColor:"#79b821" } - standardTile("fanMode", "device.thermostatFanMode", inactiveLabel: false, decoration: "flat") { + standardTile("fanMode", "device.thermostatFanMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") { state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff" state "fanOn", label:'${name}', action:"thermostat.fanCirculate", backgroundColor:"#ffffff" state "fanCirculate", label:'${name}', action:"thermostat.fanAuto", backgroundColor:"#ffffff" } - standardTile("operatingState", "device.thermostatOperatingState") { + standardTile("operatingState", "device.thermostatOperatingState", width: 2, height: 2) { state "idle", label:'${name}', backgroundColor:"#ffffff" state "heating", label:'${name}', backgroundColor:"#ffa81e" state "cooling", label:'${name}', backgroundColor:"#269bd2" } - main("temperature","operatingState") + main("thermostatMulti") details([ "temperature","tempDown","tempUp", "mode", "fanMode", "operatingState", @@ -101,6 +131,7 @@ def installed() { sendEvent(name: "thermostatMode", value: "off") sendEvent(name: "thermostatFanMode", value: "fanAuto") sendEvent(name: "thermostatOperatingState", value: "idle") + sendEvent(name: "humidity", value: 53, unit: "%") } def parse(String description) { From e98a04a1b4a61aa2c58465b4fd57565f66abed15 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Mon, 4 Jan 2016 15:21:55 -0600 Subject: [PATCH 2/8] multi: Fix x,y,z parsing error The previous axis value parsing code was very fragile. For example, this code block: def unsignedY = hexToInt(part.split("13")[1].trim()) would fail when `part` was "13xx13", where "xx" is any value. The split assumed the value "13" was present only once in the string, and everything after the "13" was the value. When "13" was part of the value this code would interpret only "xx" as the value, instead of "xx13". The new parsing code is not fragile like this. It knows exactly what bytes of the string are X, Y, and Z and parses the values correctly. --- .../smartsense-multi-sensor.groovy | 70 +++++++------------ 1 file changed, 27 insertions(+), 43 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 8dd6c94..5771b85 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -478,50 +478,34 @@ def enrollResponse() { ] } - private Map parseAxis(String description) { - log.debug "parseAxis" - def xyzResults = [x: 0, y: 0, z: 0] - def parts = description.split("2900") - parts[0] = "12" + parts[0] - parts.each { part -> - part = part.trim() - if (part.startsWith("12")) { - def unsignedX = hexToInt(part.split("12")[1].trim()) - def signedX = unsignedX > 32767 ? unsignedX - 65536 : unsignedX - 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 - 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 - 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) - - } - } - } + def hexToSignedInt = { hexVal -> + def unsignedVal = hexToInt(hexVal) + unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal + } + + def z = hexToSignedInt(description[0..3]) + def y = hexToSignedInt(description[10..13]) + def x = hexToSignedInt(description[20..23]) + def xyzResults = [x: x, y: y, z: z] + + if (device.getDataValue("manufacturer") == "SmartThings") { + // This mapping matches the current behavior of the Device Handler for the Centralite sensors + xyzResults.x = z + xyzResults.y = y + xyzResults.z = -x + } else { + // The axises reported by the Device Handler differ from the axises reported by the sensor + // This may change in the future + xyzResults.x = z + xyzResults.y = x + xyzResults.z = y + } + + log.debug "parseAxis -- ${xyzResults}" + + if (garageSensor == "Yes") + garageEvent(xyzResults.z) getXyzResult(xyzResults, description) } From c1c2431299bf9b5eea9ebafcb08d818d3eda30c2 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Mon, 4 Jan 2016 15:56:12 -0600 Subject: [PATCH 3/8] multi: Adjust threshold multiplier to prevent motion detection while idle The previous threshold multiplier was found to be too low so motion was being detected while the sensor was idle. This new value of 630 seems to produce better results. --- .../smartsense-multi-sensor.groovy | 4 ++-- 1 file changed, 2 insertions(+), 2 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 5771b85..b950034 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -381,7 +381,7 @@ def getTemperature(value) { log.debug "Refreshing Values for manufacturer: SmartThings " refreshCmds = refreshCmds + [ - /* These values of Motion Threshold Multiplier(01) and Motion Threshold (D200) + /* These values of Motion Threshold Multiplier(01) and Motion Threshold (7602) 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. @@ -392,7 +392,7 @@ def getTemperature(value) { "send 0x${device.deviceNetworkId} 1 1", "delay 400", "zcl mfg-code ${manufacturerCode}", "delay 200", - "zcl global write 0xFC02 2 0x21 {D200}", "delay 200", + "zcl global write 0xFC02 2 0x21 {7602}", "delay 200", "send 0x${device.deviceNetworkId} 1 1", "delay 400", ] From fe505ddc9fc153fea0bd50665160a39a3459fb30 Mon Sep 17 00:00:00 2001 From: Yaima Valdivia Date: Wed, 6 Jan 2016 15:24:47 -0800 Subject: [PATCH 4/8] Removed degree sign from tile https://smartthings.atlassian.net/browse/DVCSMP-1318 --- .../smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index f28323b..b439a63 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -67,7 +67,7 @@ metadata { state "setpoint", action:"raiseSetpoint", icon:"st.thermostat.thermostat-up" } valueTile("thermostatSetpoint", "device.thermostatSetpoint", width: 1, height: 1, decoration: "flat") { - state "thermostatSetpoint", label:'${currentValue}°' + state "thermostatSetpoint", label:'${currentValue}' } valueTile("currentStatus", "device.thermostatStatus", height: 1, width: 2, decoration: "flat") { state "thermostatStatus", label:'${currentValue}', backgroundColor:"#ffffff" From 9733947feacc8bb44097701ab388cf6c191d1871 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Thu, 7 Jan 2016 14:30:04 -0600 Subject: [PATCH 5/8] arrival: Add support for ZigBee HA arrival sensor Resolves: https://smartthings.atlassian.net/browse/DVCSMP-1305 https://smartthings.atlassian.net/browse/DVCSMP-1322 --- .../arrival-sensor-ha.groovy | 152 ++++++++++++++++++ 1 file changed, 152 insertions(+) create mode 100644 devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy diff --git a/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy new file mode 100644 index 0000000..c67ede5 --- /dev/null +++ b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy @@ -0,0 +1,152 @@ +/** + * Copyright 2016 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. + * + */ +metadata { + definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") { + capability "Tone" + capability "Actuator" + capability "Presence Sensor" + capability "Sensor" + capability "Battery" + capability "Configuration" + + fingerprint inClusters: "0000,0001,0003,000F,0020", outClusters: "0003,0019", + manufacturer: "SmartThings", model: "tagv4", deviceJoinName: "Arrival Sensor" + } + + preferences { + section { + image(name: 'educationalcontent', multiple: true, images: [ + "http://cdn.device-gse.smartthings.com/Arrival/Arrival1.png", + "http://cdn.device-gse.smartthings.com/Arrival/Arrival2.png" + ]) + } + section { + input "checkInterval", "enum", title: "Presence timeout (minutes)", + defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false + } + } + + tiles { + standardTile("presence", "device.presence", width: 2, height: 2, canChangeBackground: true) { + state "present", labelIcon:"st.presence.tile.present", backgroundColor:"#53a7c0" + state "not present", labelIcon:"st.presence.tile.not-present", backgroundColor:"#ffffff" + } + standardTile("beep", "device.beep", decoration: "flat") { + state "beep", label:'', action:"tone.beep", icon:"st.secondary.beep", backgroundColor:"#ffffff" + } + valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) { + state "battery", label:'${currentValue}% battery', unit:"" + } + + main "presence" + details(["presence", "beep", "battery"]) + } +} + +def updated() { + startTimer() +} + +def configure() { + def cmds = zigbee.configureReporting(0x0001, 0x0020, 0x20, 20, 20, 0x01) + log.debug "configure -- cmds: ${cmds}" + return cmds +} + +def beep() { + log.debug "Sending Identify command to beep the sensor for 5 seconds" + return zigbee.command(0x0003, 0x00, "0500") +} + +def parse(String description) { + state.lastCheckin = now() + handlePresenceEvent(true) + + if (description?.startsWith('read attr -')) { + handleReportAttributeMessage(description) + } +} + +private handleReportAttributeMessage(String description) { + def descMap = zigbee.parseDescriptionAsMap(description) + + if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) { + handleBatteryEvent(Integer.parseInt(descMap.value, 16)) + } +} + +private handleBatteryEvent(rawValue) { + def linkText = getLinkText(device) + + def eventMap = [ + name: 'battery', + value: '--' + ] + + def volts = rawValue / 10 + if (volts > 3.5) { + eventMap.descriptionText = "${linkText} battery has too much power (${volts} volts)." + } + else if (volts > 0){ + def minVolts = 2.1 + def maxVolts = 3.0 + def pct = (volts - minVolts) / (maxVolts - minVolts) + if (pct < 0) + pct = 0 + eventMap.value = Math.min(100, (int) pct * 100) + eventMap.descriptionText = "${linkText} battery was ${eventMap.value}%" + } + + log.debug "Creating battery event: ${eventMap}" + sendEvent(eventMap) +} + +private handlePresenceEvent(present) { + def wasPresent = device.currentState("presence")?.value == "present" + if (!wasPresent && present) { + log.debug "Sensor is present" + startTimer() + } else if (!present) { + log.debug "Sensor is not present" + stopTimer() + } + def linkText = getLinkText(device) + def eventMap = [ + name: "presence", + value: present ? "present" : "not present", + linkText: linkText, + descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}", + ] + log.debug "Creating presence event: ${eventMap}" + sendEvent(eventMap) +} + +private startTimer() { + log.debug "Scheduling periodic timer" + schedule("0 * * * * ?", checkPresenceCallback) +} + +private stopTimer() { + log.debug "Stopping periodic timer" + unschedule() +} + +def checkPresenceCallback() { + def timeSinceLastCheckin = (now() - state.lastCheckin) / 1000 + def theCheckInterval = (checkInterval ? checkInterval as int : 2) * 60 + log.debug "Sensor checked in ${timeSinceLastCheckin} seconds ago" + if (timeSinceLastCheckin >= theCheckInterval) { + handlePresenceEvent(false) + } +} From 26ab32565bab29a2651dbfb03d9dc8fc243f6aff Mon Sep 17 00:00:00 2001 From: Juan Risso Date: Fri, 8 Jan 2016 16:01:57 -0500 Subject: [PATCH 6/8] Increased delay to mark device as offline --- .../wemo-light-switch.src/wemo-light-switch.groovy | 2 +- devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy | 5 ++--- devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy | 7 ++++--- 3 files changed, 7 insertions(+), 7 deletions(-) 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 1bd78f3..94e2715 100644 --- a/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy +++ b/devicetypes/smartthings/wemo-light-switch.src/wemo-light-switch.groovy @@ -273,7 +273,7 @@ User-Agent: CyberGarage-HTTP/1.0 def poll() { log.debug "Executing 'poll'" if (device.currentValue("currentIP") != "Offline") - runIn(10, setOffline) + runIn(30, setOffline) new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" Content-Length: 277 diff --git a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy index 9649db4..b7fbee1 100644 --- a/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy +++ b/devicetypes/smartthings/wemo-motion.src/wemo-motion.groovy @@ -77,9 +77,8 @@ def parse(String description) { def result = [] def bodyString = msg.body if (bodyString) { - unschedule("setOffline") + unschedule("setOffline") def body = new XmlSlurper().parseText(bodyString) - if (body?.property?.TimeSyncRequest?.text()) { log.trace "Got TimeSyncRequest" result << timeSyncResponse() @@ -134,7 +133,7 @@ def refresh() { def getStatus() { log.debug "Executing WeMo Motion 'getStatus'" if (device.currentValue("currentIP") != "Offline") - runIn(10, setOffline) + runIn(30, setOffline) new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" Content-Length: 277 diff --git a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy index cd9e0ec..6ac3939 100644 --- a/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy +++ b/devicetypes/smartthings/wemo-switch.src/wemo-switch.groovy @@ -28,6 +28,7 @@ command "subscribe" command "resubscribe" command "unsubscribe" + command "setOffline" } // simulator metadata @@ -207,7 +208,7 @@ def subscribe(ip, port) { def existingIp = getDataValue("ip") def existingPort = getDataValue("port") if (ip && ip != existingIp) { - log.debug "Updating ip from $existingIp to $ip" + 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}") @@ -275,7 +276,7 @@ def setOffline() { def poll() { log.debug "Executing 'poll'" if (device.currentValue("currentIP") != "Offline") - runIn(10, setOffline) + runIn(30, setOffline) new physicalgraph.device.HubAction("""POST /upnp/control/basicevent1 HTTP/1.1 SOAPACTION: "urn:Belkin:service:basicevent:1#GetBinaryState" Content-Length: 277 @@ -290,4 +291,4 @@ User-Agent: CyberGarage-HTTP/1.0 """, physicalgraph.device.Protocol.LAN) -} +} \ No newline at end of file From 0d214b742e1cd840d638aa1592134c595894eb87 Mon Sep 17 00:00:00 2001 From: Juan Risso Date: Mon, 11 Jan 2016 12:10:13 -0500 Subject: [PATCH 7/8] PROB-537 - Fix error in line 337 --- smartapps/smartthings/hue-connect.src/hue-connect.groovy | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 7d81c41..053c266 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -258,8 +258,8 @@ def installed() { def updated() { log.trace "Updated with settings: ${settings}" - unschedule() unsubscribe() + unschedule() initialize() } @@ -325,6 +325,7 @@ def addBulbs() { } else { d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.value.hub, ["label":newHueBulb?.value.name]) } + log.debug "created ${d.displayName} with id $dni" } else { log.debug "$dni in not longer paired to the Hue Bridge or ID changed" } @@ -333,8 +334,6 @@ def addBulbs() { newHueBulb = bulbs.find { (app.id + "/" + it.id) == dni } d = addChildDevice("smartthings", "Hue Bulb", dni, newHueBulb?.hub, ["label":newHueBulb?.name]) } - - log.debug "created ${d.displayName} with id $dni" d.refresh() } else { log.debug "found ${d.displayName} with id $dni already exists, type: '$d.typeName'" @@ -775,4 +774,4 @@ private Boolean hasAllHubsOver(String desiredFirmware) { private List getRealHubFirmwareVersions() { return location.hubs*.firmwareVersionString.findAll { it } -} +} \ No newline at end of file From 112a35f5db8f5f83b4abbe2cffdef5c9c5b99ed6 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Mon, 11 Jan 2016 12:41:50 -0600 Subject: [PATCH 8/8] arrival: Change voltage range for battery remaining calculation --- .../arrival-sensor-ha.groovy | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy index c67ede5..d9cdcc2 100644 --- a/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy +++ b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy @@ -95,16 +95,17 @@ private handleBatteryEvent(rawValue) { ] def volts = rawValue / 10 - if (volts > 3.5) { - eventMap.descriptionText = "${linkText} battery has too much power (${volts} volts)." - } - else if (volts > 0){ - def minVolts = 2.1 - def maxVolts = 3.0 + if (volts > 0){ + def minVolts = 2.0 + def maxVolts = 2.8 + + if (volts < minVolts) + volts = minVolts + else if (volts > maxVolts) + volts = maxVolts def pct = (volts - minVolts) / (maxVolts - minVolts) - if (pct < 0) - pct = 0 - eventMap.value = Math.min(100, (int) pct * 100) + + eventMap.value = Math.round(pct * 100) eventMap.descriptionText = "${linkText} battery was ${eventMap.value}%" }