From 358cf261e8aca3bebf0141973e73180e763e42f4 Mon Sep 17 00:00:00 2001 From: Matt Pennig Date: Tue, 22 Dec 2015 11:14:53 -0600 Subject: [PATCH 1/3] 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 9733947feacc8bb44097701ab388cf6c191d1871 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Thu, 7 Jan 2016 14:30:04 -0600 Subject: [PATCH 2/3] 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 112a35f5db8f5f83b4abbe2cffdef5c9c5b99ed6 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Mon, 11 Jan 2016 12:41:50 -0600 Subject: [PATCH 3/3] 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}%" }