Compare commits

..

24 Commits

Author SHA1 Message Date
Stuart
1b0192d9e3 MSA-916: This Device Type and SmartApp integrated the Garadget Device Type enable integration of Smartthings With the Garadget Devices 2016-03-03 12:54:29 -06:00
Yaima
8ed23f4c7e Merge pull request #577 from Yaima/master
Including unknown temperature values as part of the response for sensors
2016-03-02 14:24:04 -08:00
Yaima Valdivia
e7e6ea7d56 Including unknown temperature values as part of the response for sensors
https://smartthings.atlassian.net/browse/DVCSMP-1511
2016-03-02 14:20:44 -08:00
Vinay Rao
ab4e8a892a Merge pull request #572 from workingmonk/bug/battery_values
[DVCSMP-1255] Fixing issue with weird battery values
2016-03-01 17:01:27 -08:00
Vinay Rao
e076818573 Merge pull request #573 from workingmonk/deprecate_DTH
[DVCSMP-1463] Deprecating copied DTH
2016-03-01 16:57:50 -08:00
Vinay Rao
cd8bbca5ee removing the fingerprints from the additional copy of DTH 2016-03-01 12:46:16 -08:00
Vinay Rao
2d060bddfc fixing issue with weird battery values 2016-03-01 12:17:50 -08:00
Dylan Bijnagte
4da9730319 Merge pull request #550 from Bijnagte/device-translations
adding korean translations for DTHs
2016-03-01 13:57:05 -06:00
Donald C. Kirker
25db4f5235 Merge pull request #571 from SmartThingsCommunity/DVCSMP-1440
Remove 3axis tile definition from 1st gen multi.
2016-03-01 10:58:09 -08:00
Donald Kirker
eae2a9ca08 Remove 3axis tile definition from 1st gen multi. 2016-03-01 10:53:42 -08:00
Yaima
2dd2d7cba4 Merge pull request #569 from Yaima/master
Ecobee multiple sensors fix
2016-02-29 14:50:45 -08:00
Yaima Valdivia
f5708bca8b Copyright - Ecobee Sensor 2016-02-29 14:49:50 -08:00
Yaima Valdivia
a9da6d130a Changed copyright information for Ecobee Sensor
https://smartthings.atlassian.net/browse/DVCSMP-1511
2016-02-29 14:49:02 -08:00
Yaima Valdivia
3a2c6f86be Merge branch 'master' of github.com:SmartThingsCommunity/SmartThingsPublic
# By Lars Finander
# Via Lars Finander
* 'master' of github.com:SmartThingsCommunity/SmartThingsPublic:
  DVCSMP-1516 Hue (Connect) throws 15k groovy.lang.MissingPropertyException per day
2016-02-29 14:39:36 -08:00
Yaima Valdivia
71d2b89a37 Ecobee multiple sensors fix
https://smartthings.atlassian.net/browse/DVCSMP-1511
2016-02-29 14:15:56 -08:00
Lars Finander
b131ba1507 Merge pull request #568 from larsfinander/hueConnectFix
DVCSMP-1516 Hue (Connect) throws 15k groovy.lang.MissingPropertyExcep…
2016-02-29 13:48:07 -08:00
Lars Finander
f04a9e3f7a DVCSMP-1516 Hue (Connect) throws 15k groovy.lang.MissingPropertyException per day
-Only parse messages with JSON if they are from Hue bridge
2016-02-29 13:41:58 -08:00
bflorian
6a905e4380 Merge pull request #567 from bflorian/DVCSMP-1531-hue-command-types
DVCSMP-1531 switch hue command methods to void
2016-02-29 14:37:16 -05:00
bflorian
442f16680d DVCSMP-1531 switch hue command methods to void 2016-02-29 14:11:51 -05:00
Yaima
1c68099b52 Merge pull request #562 from Yaima/master
Fix fan mode notifications
2016-02-26 12:54:54 -08:00
Yaima
cc9321ca9f Fix fan mode notifications
https://smartthings.atlassian.net/browse/DVCSMP-1511
2016-02-26 12:39:32 -08:00
Vinay Rao
919c9b88ee Merge pull request #555 from workingmonk/lock_fingerprinting_form
[DEVTOOLS-506] Fixing lock fingerprint causing Fingerprint Tool issue
2016-02-26 09:25:32 -08:00
Vinay Rao
2438942071 Fixing lock fingerprint causing Fingerprint Tool issue 2016-02-25 20:07:05 -08:00
dylanbijnagte
d86dcfd82f adding korean translations for DTHs 2016-02-24 14:34:52 -06:00
20 changed files with 1621 additions and 719 deletions

View File

@@ -0,0 +1,406 @@
/**
* Garadget Device Handler
*
* Copyright 2016 Stuart Buchanan based loosely based on original code by Krishnaraj Varma with thanks
*
* 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.
*
* 12/02/2016 V1.3 updated with to remove token and DeviceId parameters from inputs to retrieving from dni
*/
import groovy.json.JsonOutput
preferences {
input("prdt", "text", title: "sensor scan interval in mS (default: 1000)")
input("pmtt", "text", title: "door moving time in mS(default: 10000)")
input("prlt", "text", title: "button press time mS (default: 300)")
input("prlp", "text", title: "delay between consecutive button presses in mS (default: 1000)")
input("psrr", "text", title: "number of sensor reads used in averaging (default: 3)")
input("psrt", "text", title: "reflection threshold below which the door is considered open (default: 25)")
input("paot", "text", title: "alert for open timeout in seconds (default: 320)")
input("pans", "text", title: " alert for night time start in minutes from midnight (default: 1320)")
input("pane", "text", title: " alert for night time end in minutes from midnight (default: 360)")
}
metadata {
definition (name: "Garadget", namespace: "fuzzysb", author: "Stuart Buchanan") {
capability "Switch"
capability "Contact Sensor"
capability "Signal Strength"
capability "Actuator"
capability "Sensor"
capability "Refresh"
capability "Polling"
capability "Configuration"
attribute "reflection", "string"
attribute "status", "string"
attribute "time", "string"
attribute "lastAction", "string"
attribute "reflection", "string"
attribute "ver", "string"
command "stop"
command "statusCommand"
command "setConfigCommand"
command "doorConfigCommand"
command "netConfigCommand"
}
simulator {
}
tiles(scale: 2) {
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
attributeState "open", label:'${name}', action:"switch.off", icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e"
attributeState "opening", label:'${name}', icon:"st.doors.garage.garage-opening", backgroundColor:"#ffa81e"
attributeState "closing", label:'${name}', icon:"st.doors.garage.garage-closing", backgroundColor:"#6699ff"
attributeState "closed", label:'${name}', action:"switch.on", icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821"
}
tileAttribute ("device.lastAction", key: "SECONDARY_CONTROL") {
attributeState "default", label: 'Time In State: ${currentValue}'
}
}
standardTile("contact", "device.contact", width: 1, height: 1) {
state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
}
valueTile("reflection", "reflection", decoration: "flat", width: 2, height: 1) {
state "reflection", label:'Reflection\r\n${currentValue}%'
}
valueTile("rssi", "device.rssi", decoration: "flat", width: 1, height: 1) {
state "rssi", label:'Wifi\r\n${currentValue} dBm', unit: "",backgroundColors:[
[value: 16, color: "#5600A3"],
[value: -31, color: "#153591"],
[value: -44, color: "#1e9cbb"],
[value: -59, color: "#90d2a7"],
[value: -74, color: "#44b621"],
[value: -84, color: "#f1d801"],
[value: -95, color: "#d04e00"],
[value: -96, color: "#bc2323"]
]
}
standardTile("refresh", "refresh", inactiveLabel: false, decoration: "flat") {
state "default", action:"polling.poll", icon:"st.secondary.refresh"
}
standardTile("stop", "stop") {
state "default", label:"", action: "stop", icon:"http://cdn.device-icons.smartthings.com/sonos/stop-btn@2x.png"
}
valueTile("ip", "ip", decoration: "flat", width: 2, height: 1) {
state "ip", label:'IP Address\r\n${currentValue}'
}
valueTile("ssid", "ssid", decoration: "flat", width: 2, height: 1) {
state "ssid", label:'Wifi SSID\r\n${currentValue}'
}
valueTile("ver", "ver", decoration: "flat", width: 1, height: 1) {
state "ver", label:'Version\r\n${currentValue}'
}
standardTile("configure", "device.button", width: 1, height: 1, decoration: "flat") {
state "default", label: "", backgroundColor: "#ffffff", action: "configure", icon:"st.secondary.configure"
}
main "status"
details(["status", "contact", "reflection", "ver", "configure", "lastAction", "rssi", "stop", "ip", "ssid", "refresh"])
}
}
// handle commands
def poll() {
log.debug "Executing 'poll'"
refresh()
}
def refresh() {
log.debug "Executing 'refresh'"
statusCommand()
netConfigCommand()
doorConfigCommand()
}
def configure() {
log.debug "Resetting Sensor Parameters to SmartThings Compatible Defaults"
SetConfigCommand()
}
// Parse incoming device messages to generate events
private parseDoorStatusResponse(resp) {
log.debug("Executing parseDoorStatusResponse: "+resp.data)
log.debug("Output status: "+resp.status)
if(resp.status == 200) {
log.debug("returnedresult: "+resp.data.result)
def results = (resp.data.result).tokenize('|')
def statusvalues = (results[0]).tokenize('=')
def timevalues = (results[1]).tokenize('=')
def sensorvalues = (results[2]).tokenize('=')
def signalvalues = (results[3]).tokenize('=')
def status = statusvalues[1]
sendEvent(name: 'status', value: status)
if(status == "open" || status == "closed"){
sendEvent(name: 'contact', value: status)
}
def time = timevalues[1]
sendEvent(name: 'lastAction', value: time)
def sensor = sensorvalues[1]
sendEvent(name: 'reflection', value: sensor)
def signal = signalvalues[1]
sendEvent(name: 'rssi', value: signal)
}else if(resp.status == 201){
log.debug("Something was created/updated")
}
}
private parseDoorConfigResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
if(resp.status == 200) {
log.debug("returnedresult: "+resp.data.result)
def results = (resp.data.result).tokenize('|')
def vervalues = (results[0]).tokenize('=')
def rdtvalues = (results[1]).tokenize('=')
def mttvalues = (results[2]).tokenize('=')
def rltvalues = (results[3]).tokenize('=')
def rlpvalues = (results[4]).tokenize('=')
def srrvalues = (results[5]).tokenize('=')
def srtvalues = (results[6]).tokenize('=')
def aotvalues = (results[7]).tokenize('=')
def ansvalues = (results[8]).tokenize('=')
def anevalues = (results[9]).tokenize('=')
def ver = vervalues[1]
sendEvent(name: 'ver', value: ver)
log.debug("Firmware Version: "+ver)
def rdt = rdtvalues[1]
log.debug("Sensor Scan Interval (ms): "+rdt )
def mtt = mttvalues[1]
state.mtt = mtt
sendEvent(name: 'mtt', value: mtt)
log.debug("Door Moving Time (ms): "+mtt )
def rlt = rltvalues[1]
log.debug("Button Press Time (ms): "+rlt )
def rlp = rlpvalues[1]
log.debug("Delay Between Consecutive Button Presses (ms): "+rlp )
def srr = srrvalues[1]
log.debug("number of sensor reads used in averaging: "+srr )
def srt = srtvalues[1]
log.debug("reflection threshold below which the door is considered open: "+srt )
def aot = aotvalues[1]
log.debug("alert for open timeout in seconds: "+aot )
def ans = ansvalues[1]
log.debug("alert for night time start in minutes from midnight: "+ans )
def ane = anevalues[1]
log.debug("alert for night time end in minutes from midnight: "+ane )
}else if(resp.status == 201){
log.debug("Something was created/updated")
}
}
private parseNetConfigResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
if(resp.status == 200) {
log.debug("returnedresult: "+resp.data.result)
def results = (resp.data.result).tokenize('|')
def ipvalues = (results[0]).tokenize('=')
def snetvalues = (results[1]).tokenize('=')
def dgwvalues = (results[2]).tokenize('=')
def macvalues = (results[3]).tokenize('=')
def ssidvalues = (results[4]).tokenize('=')
def ip = ipvalues[1]
sendEvent(name: 'ip', value: ip)
log.debug("IP Address: "+ip)
def snet = snetvalues[1]
log.debug("Subnet Mask: "+snet)
def dgw = dgwvalues[1]
log.debug("Default Gateway: "+dgw)
def mac = macvalues[1]
log.debug("Mac Address: "+mac)
def ssid = ssidvalues[1]
sendEvent(name: 'ssid', value: ssid)
log.debug("Wifi SSID : "+ssid)
}else if(resp.status == 201){
log.debug("Something was created/updated")
}
}
private parseResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
if(resp.status == 200) {
log.debug("Executing parseResponse.successTrue")
def id = resp.data.id
def name = resp.data.name
def connected = resp.data.connected
def returnValue = resp.data.return_value
}else if(resp.status == 201){
log.debug("Something was created/updated")
}
}
private getDeviceDetails() {
def fullDni = device.deviceNetworkId
return fullDni
}
private sendCommand(method, args = []) {
def DefaultUri = "https://api.particle.io"
def cdni = getDeviceDetails().tokenize(':')
def deviceId = cdni[0]
def token = cdni[1]
def methods = [
'doorStatus': [
uri: "${DefaultUri}",
path: "/v1/devices/${deviceId}/doorStatus",
requestContentType: "application/json",
query: [access_token: token]
],
'doorConfig': [
uri: "${DefaultUri}",
path: "/v1/devices/${deviceId}/doorConfig",
requestContentType: "application/json",
query: [access_token: token]
],
'netConfig': [
uri: "${DefaultUri}",
path: "/v1/devices/${deviceId}/netConfig",
requestContentType: "application/json",
query: [access_token: token]
],
'setState': [
uri: "${DefaultUri}",
path: "/v1/devices/${deviceId}/setState",
requestContentType: "application/json",
query: [access_token: token],
body: args[0]
],
'setConfig': [
uri: "${DefaultUri}",
path: "/v1/devices/${deviceId}/setConfig",
requestContentType: "application/json",
query: [access_token: token],
body: args[0]
]
]
def request = methods.getAt(method)
log.debug "Http Params ("+request+")"
try{
log.debug "Executing 'sendCommand'"
if (method == "doorStatus"){
httpGet(request) { resp ->
parseDoorStatusResponse(resp)
}
}else if (method == "doorConfig"){
log.debug "calling doorConfig Method"
httpGet(request) { resp ->
parseDoorConfigResponse(resp)
}
}else if (method == "netConfig"){
log.debug "calling netConfig Method"
httpGet(request) { resp ->
parseNetConfigResponse(resp)
}
}else if (method == "setState"){
log.debug "calling setState Method"
httpPost(request) { resp ->
parseResponse(resp)
}
}else if (method == "setConfig"){
log.debug "calling setState Method"
httpPost(request) { resp ->
parseResponse(resp)
}
}else{
httpGet(request)
}
} catch(Exception e){
log.debug("___exception: " + e)
}
}
def on() {
log.debug "Executing 'on'"
openCommand()
statusCommand()
log.info("waiting for ${state.mtt} ms")
"delay ${state.mtt}"
log.info("Initiating Refresh after Transition time")
statusCommand()
}
def off() {
log.debug "Executing 'off'"
closeCommand()
statusCommand()
log.info("waiting for ${state.mtt} ms")
"delay ${state.mtt}"
log.info("Initiating Refresh after Transition time")
statusCommand()
}
def stop(){
log.debug "Executing 'sendCommand.setState'"
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"stop")
sendCommand("setState",[jsonbody])
statusCommand()
}
def statusCommand(){
log.debug "Executing 'sendCommand.statusCommand'"
sendCommand("doorStatus",[])
}
def openCommand(){
log.debug "Executing 'sendCommand.setState'"
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"open")
sendCommand("setState",[jsonbody])
}
def closeCommand(){
log.debug "Executing 'sendCommand.setState'"
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"close")
sendCommand("setState",[jsonbody])
}
def doorConfigCommand(){
log.debug "Executing 'sendCommand.doorConfig'"
sendCommand("doorConfig",[])
}
def SetConfigCommand(){
def crdt = prdt ?: 1000
def cmtt = pmtt ?: 10000
def crlt = prlt ?: 300
def crlp = prlp ?: 1000
def csrr = psrr ?: 3
def csrt = psrt ?: 25
def caot = paot ?: 320
def cans = pans ?: 1320
def cane = pane ?: 360
log.debug "Executing 'sendCommand.setConfig'"
def jsonbody = new groovy.json.JsonOutput().toJson(arg:"rdt=" + crdt +"|mtt=" + cmtt + "|rlt=" + crlt + "|rlp=" + crlp +"|srr=" + csrr + "|srt=" + csrt)
sendCommand("setConfig",[jsonbody])
jsonbody = new groovy.json.JsonOutput().toJson(arg:"aot=" + caot + "|ans=" + cans + "|ane=" + cane)
sendCommand("setConfig",[jsonbody])
}
def netConfigCommand(){
log.debug "Executing 'sendCommand.netConfig'"
sendCommand("netConfig",[])
}

View File

@@ -1,294 +1,294 @@
/**
* 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.
*
*/
metadata {
definition (name: "Arrival Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Tone"
capability "Actuator"
capability "Signal Strength"
capability "Presence Sensor"
capability "Sensor"
capability "Battery"
fingerprint profileId: "FC01", deviceId: "019A"
fingerprint profileId: "FC01", deviceId: "0131", inClusters: "0000,0003", outClusters: "0003"
fingerprint profileId: "FC01", deviceId: "0131", inClusters: "0000", outClusters: "0006"
}
simulator {
status "present": "presence: 1"
status "not present": "presence: 0"
status "battery": "battery: 27, batteryDivisor: 0A, rssi: 100, lqi: 64"
}
preferences {
section {
image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.device-gse.smartthings.com/Arrival/Arrival1.jpg",
"http://cdn.device-gse.smartthings.com/Arrival/Arrival2.jpg"
])
}
}
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:"#ebeef2"
}
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:""/*, backgroundColors:[
[value: 5, color: "#BC2323"],
[value: 10, color: "#D04E00"],
[value: 15, color: "#F1D801"],
[value: 16, color: "#FFFFFF"]
]*/
}
/*
valueTile("lqi", "device.lqi", decoration: "flat", inactiveLabel: false) {
state "lqi", label:'${currentValue}% signal', unit:""
}
*/
main "presence"
details(["presence", "beep", "battery"/*, "lqi"*/])
}
}
def beep() {
/*
You can make the speaker turn on for 0.5-second beeps by sending some CLI commands:
Command: send raw, wait 7, send raw, wait 7, send raw
Future: new packet type "st.beep"
raw 0xFC05 {15 0A 11 00 00 15 01}
send 0x2F7F 2 2
where "0xABCD" is the node ID of the Smart Tag, everything else above is a constant. Except
the "15 01" at the end of the first raw command, that sets the speaker's period (reciprocal
of frequency). You can play with this value up or down to experiment with loudness as the
loudness will be strongly dependent upon frequency and the enclosure that it's in. Note that
"15 01" represents the hex number 0x0115 so a lower frequency is "16 01" (longer period) and
a higher frequency is "14 01" (shorter period). Note that since the tag only checks its parent
for messages every 5 seconds (while at rest) or every 3 seconds (while in motion) it will take
up to this long from the time you send the message to the time you hear a sound.
*/
[
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}"
]
}
def parse(String description) {
def results
if (isBatteryMessage(description)) {
results = parseBatteryMessage(description)
}
else {
results = parsePresenceMessage(description)
}
log.debug "Parse returned $results.descriptionText"
results
}
private Map parsePresenceMessage(String description) {
def name = parseName(description)
def value = parseValue(description)
def linkText = getLinkText(device)
def descriptionText = parseDescriptionText(linkText, value, description)
def handlerName = getState(value)
def isStateChange = isStateChange(device, name, value)
def results = [
name: name,
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
]
results
}
private String parseName(String description) {
if (description?.startsWith("presence: ")) {
return "presence"
}
null
}
private String parseValue(String description) {
if (description?.startsWith("presence: "))
{
if (description?.endsWith("1"))
{
return "present"
}
else if (description?.endsWith("0"))
{
return "not present"
}
}
description
}
private parseDescriptionText(String linkText, String value, String description) {
switch(value) {
case "present": return "$linkText has arrived"
case "not present": return "$linkText has left"
default: return value
}
}
private getState(String value) {
def state = value
if (value == "present") {
state = "arrived"
}
else if (value == "not present") {
state = "left"
}
state
}
private Boolean isBatteryMessage(String description) {
// "raw:36EF1C, dni:36EF, battery:1B, rssi:, lqi:"
description ==~ /.*battery:.*rssi:.*lqi:.*/
}
private List parseBatteryMessage(String description) {
def results = []
def parts = description.split(',')
parts.each { part ->
part = part.trim()
if (part.startsWith('battery:')) {
def batteryResult = getBatteryResult(part, description)
if (batteryResult) {
results << batteryResult
}
}
else if (part.startsWith('rssi:')) {
def rssiResult = getRssiResult(part, description)
if (rssiResult) {
results << rssiResult
}
}
else if (part.startsWith('lqi:')) {
def lqiResult = getLqiResult(part, description)
if (lqiResult) {
results << lqiResult
}
}
}
results
}
private getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
def unit = "%"
def linkText = getLinkText(device)
def descriptionText = "$linkText battery was ${value}${unit}"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: unit,
linkText: linkText,
descriptionText: descriptionText,
handlerName: name,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}
private getRssiResult(part, description) {
def name = "rssi"
def parts = part.split(":")
if (parts.size() != 2) return null
def valueString = parts[1].trim()
def valueInt = Integer.parseInt(valueString, 16)
def value = (valueInt - 128).toString()
def linkText = getLinkText(device)
def descriptionText = "$linkText was $value dBm"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: "dBm",
linkText: linkText,
descriptionText: descriptionText,
handlerName: null,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}
/**
* Use LQI (Link Quality Indicator) as a measure of signal strength. The values
* are 0 to 255 (0x00 to 0xFF) and higher values represent higher signal
* strength. Return as a percentage of 255.
*
* Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI.
*/
private getLqiResult(part, description) {
def name = "lqi"
def parts = part.split(":")
if (parts.size() != 2) return null
def valueString = parts[1].trim()
def valueInt = Integer.parseInt(valueString, 16)
def percentageOf = 255
def value = Math.round((valueInt / percentageOf * 100)).toString()
def unit = "%"
def linkText = getLinkText(device)
def descriptionText = "$linkText Signal (LQI) was ${value}${unit}"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: unit,
linkText: linkText,
descriptionText: descriptionText,
handlerName: null,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}
/**
* 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.
*
*/
metadata {
definition (name: "Arrival Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Tone"
capability "Actuator"
capability "Signal Strength"
capability "Presence Sensor"
capability "Sensor"
capability "Battery"
fingerprint profileId: "FC01", deviceId: "019A"
fingerprint profileId: "FC01", deviceId: "0131", inClusters: "0000,0003", outClusters: "0003"
fingerprint profileId: "FC01", deviceId: "0131", inClusters: "0000", outClusters: "0006"
}
simulator {
status "present": "presence: 1"
status "not present": "presence: 0"
status "battery": "battery: 27, batteryDivisor: 0A, rssi: 100, lqi: 64"
}
preferences {
section {
image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.device-gse.smartthings.com/Arrival/Arrival1.jpg",
"http://cdn.device-gse.smartthings.com/Arrival/Arrival2.jpg"
])
}
}
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:"#ebeef2"
}
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:""/*, backgroundColors:[
[value: 5, color: "#BC2323"],
[value: 10, color: "#D04E00"],
[value: 15, color: "#F1D801"],
[value: 16, color: "#FFFFFF"]
]*/
}
/*
valueTile("lqi", "device.lqi", decoration: "flat", inactiveLabel: false) {
state "lqi", label:'${currentValue}% signal', unit:""
}
*/
main "presence"
details(["presence", "beep", "battery"/*, "lqi"*/])
}
}
def beep() {
/*
You can make the speaker turn on for 0.5-second beeps by sending some CLI commands:
Command: send raw, wait 7, send raw, wait 7, send raw
Future: new packet type "st.beep"
raw 0xFC05 {15 0A 11 00 00 15 01}
send 0x2F7F 2 2
where "0xABCD" is the node ID of the Smart Tag, everything else above is a constant. Except
the "15 01" at the end of the first raw command, that sets the speaker's period (reciprocal
of frequency). You can play with this value up or down to experiment with loudness as the
loudness will be strongly dependent upon frequency and the enclosure that it's in. Note that
"15 01" represents the hex number 0x0115 so a lower frequency is "16 01" (longer period) and
a higher frequency is "14 01" (shorter period). Note that since the tag only checks its parent
for messages every 5 seconds (while at rest) or every 3 seconds (while in motion) it will take
up to this long from the time you send the message to the time you hear a sound.
*/
[
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}"
]
}
def parse(String description) {
def results
if (isBatteryMessage(description)) {
results = parseBatteryMessage(description)
}
else {
results = parsePresenceMessage(description)
}
log.debug "Parse returned $results.descriptionText"
results
}
private Map parsePresenceMessage(String description) {
def name = parseName(description)
def value = parseValue(description)
def linkText = getLinkText(device)
def descriptionText = parseDescriptionText(linkText, value, description)
def handlerName = getState(value)
def isStateChange = isStateChange(device, name, value)
def results = [
name: name,
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
]
results
}
private String parseName(String description) {
if (description?.startsWith("presence: ")) {
return "presence"
}
null
}
private String parseValue(String description) {
if (description?.startsWith("presence: "))
{
if (description?.endsWith("1"))
{
return "present"
}
else if (description?.endsWith("0"))
{
return "not present"
}
}
description
}
private parseDescriptionText(String linkText, String value, String description) {
switch(value) {
case "present": return "$linkText has arrived"
case "not present": return "$linkText has left"
default: return value
}
}
private getState(String value) {
def state = value
if (value == "present") {
state = "arrived"
}
else if (value == "not present") {
state = "left"
}
state
}
private Boolean isBatteryMessage(String description) {
// "raw:36EF1C, dni:36EF, battery:1B, rssi:, lqi:"
description ==~ /.*battery:.*rssi:.*lqi:.*/
}
private List parseBatteryMessage(String description) {
def results = []
def parts = description.split(',')
parts.each { part ->
part = part.trim()
if (part.startsWith('battery:')) {
def batteryResult = getBatteryResult(part, description)
if (batteryResult) {
results << batteryResult
}
}
else if (part.startsWith('rssi:')) {
def rssiResult = getRssiResult(part, description)
if (rssiResult) {
results << rssiResult
}
}
else if (part.startsWith('lqi:')) {
def lqiResult = getLqiResult(part, description)
if (lqiResult) {
results << lqiResult
}
}
}
results
}
private getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
def unit = "%"
def linkText = getLinkText(device)
def descriptionText = "$linkText battery was ${value}${unit}"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: unit,
linkText: linkText,
descriptionText: descriptionText,
handlerName: name,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}
private getRssiResult(part, description) {
def name = "rssi"
def parts = part.split(":")
if (parts.size() != 2) return null
def valueString = parts[1].trim()
def valueInt = Integer.parseInt(valueString, 16)
def value = (valueInt - 128).toString()
def linkText = getLinkText(device)
def descriptionText = "$linkText was $value dBm"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: "dBm",
linkText: linkText,
descriptionText: descriptionText,
handlerName: null,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}
/**
* Use LQI (Link Quality Indicator) as a measure of signal strength. The values
* are 0 to 255 (0x00 to 0xFF) and higher values represent higher signal
* strength. Return as a percentage of 255.
*
* Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI.
*/
private getLqiResult(part, description) {
def name = "lqi"
def parts = part.split(":")
if (parts.size() != 2) return null
def valueString = parts[1].trim()
def valueInt = Integer.parseInt(valueString, 16)
def percentageOf = 255
def value = Math.round((valueInt / percentageOf * 100)).toString()
def unit = "%"
def linkText = getLinkText(device)
def descriptionText = "$linkText Signal (LQI) was ${value}${unit}"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: unit,
linkText: linkText,
descriptionText: descriptionText,
handlerName: null,
isStateChange: isStateChange,
//displayed: displayed(description, isStateChange)
displayed: false
]
}

View File

@@ -1,7 +1,5 @@
/**
* Ecobee Sensor
*
* Copyright 2015 Juan Risso
* 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:
@@ -12,6 +10,9 @@
* 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.
*
* Ecobee Sensor
*
* Author: SmartThings
*/
metadata {
definition (name: "Ecobee Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -26,7 +27,16 @@ metadata {
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', unit:"F",
backgroundColors:[
[value: 31, color: "#153591"],
// Celsius
[value: 0, color: "#153591"],
[value: 7, color: "#1e9cbb"],
[value: 15, color: "#90d2a7"],
[value: 23, color: "#44b621"],
[value: 28, color: "#f1d801"],
[value: 35, color: "#d04e00"],
[value: 37, color: "#bc2323"],
// Fahrenheit
[value: 40, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
@@ -38,8 +48,8 @@ metadata {
}
standardTile("motion", "device.motion") {
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
state("inactive", label:'no motion', icon:"st.motion.motion.inactive", backgroundColor:"#ffffff")
state("active", label:'motion', icon:"st.motion.motion.active", backgroundColor:"#53a7c0")
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {

View File

@@ -395,7 +395,7 @@ def generateModeEvent(mode) {
}
def generateFanModeEvent(fanMode) {
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${mode} mode", displayed: true)
sendEvent(name: "thermostatFanMode", value: fanMode, descriptionText: "$device.displayName fan is in ${fanMode} mode", displayed: true)
}
def generateOperatingStateEvent(operatingState) {
@@ -493,7 +493,7 @@ def fanOn() {
} else {
log.debug "Error setting new mode."
def currentFanMode = device.currentState("thermostatFanMode")?.value
generateModeEvent(currentFanMode) // reset the tile back
generateFanModeEvent(currentFanMode) // reset the tile back
}
}
@@ -514,7 +514,7 @@ def fanAuto() {
} else {
log.debug "Error setting new mode."
def currentFanMode = device.currentState("thermostatFanMode")?.value
generateModeEvent(currentFanMode) // reset the tile back
generateFanModeEvent(currentFanMode) // reset the tile back
}
}

View File

@@ -16,8 +16,8 @@ metadata {
capability "Sensor"
command "setAdjustedColor"
command "reset"
command "refresh"
command "reset"
command "refresh"
}
simulator {
@@ -68,17 +68,17 @@ def parse(description) {
}
// handle commands
def on() {
void on() {
log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
def off() {
void off() {
log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
def nextLevel() {
void nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
@@ -89,25 +89,25 @@ def nextLevel() {
setLevel(level)
}
def setLevel(percent) {
void setLevel(percent) {
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
}
def setSaturation(percent) {
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent)
}
def setHue(percent) {
void setHue(percent) {
log.debug "Executing 'setHue'"
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent)
}
def setColor(value) {
void setColor(value) {
log.debug "setColor: ${value}, $this"
parent.setColor(this, value)
if (value.hue) { sendEvent(name: "hue", value: value.hue)}
@@ -117,25 +117,25 @@ def setColor(value) {
if (value.switch) { sendEvent(name: "switch", value: value.switch)}
}
def reset() {
void reset() {
log.debug "Executing 'reset'"
def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value)
def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}
def setAdjustedColor(value) {
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
}
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
}
}
def refresh() {
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}

View File

@@ -12,48 +12,48 @@ metadata {
capability "Switch"
capability "Refresh"
capability "Sensor"
command "refresh"
command "refresh"
}
simulator {
// TODO: define status and reply messages here
}
tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
tiles(scale: 2) {
multiAttributeTile(name:"rich-control", type: "lighting", canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
}
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
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"])
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
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
@@ -74,23 +74,23 @@ def parse(description) {
}
// handle commands
def on() {
void on() {
parent.on(this)
sendEvent(name: "switch", value: "on")
}
def off() {
void off() {
parent.off(this)
sendEvent(name: "switch", value: "off")
}
def setLevel(percent) {
void setLevel(percent) {
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
}
def refresh() {
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}

View File

@@ -0,0 +1,31 @@
#==============================================================================
# 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.
#==============================================================================
# Purpose: Mobile Presence i18n Translation File
#
# Filename: mobile-presence.src/i18n/messages.properties
#
# Change History:
# 1. 20160205 TW Initial release with informal Korean translation.
#==============================================================================
# Korean (ko)
# Device Preferences
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Set Device Image'''.ko=디바이스 이미지 설정
# Events / Notifications
'''{{ linkText }} has left'''.ko={{ linkText }}님이 나갔습니다
'''{{ linkText }} has arrived'''.ko={{ linkText }}님이 도착했습니다
'''present'''.ko=집안
'''not present'''.ko=부재중

View File

@@ -0,0 +1,14 @@
# Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Degrees'''.ko=온도
'''Temperature Offset'''.ko=온도 직접 설정
'''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''battery'''.ko=배터리
'''dry'''.ko=건조
'''wet'''.ko=누수
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }} 배터리가 {{ value }}였습니다
'''{{ device.displayName }} is {{ value | translate }}'''.ko={{ device.displayName }}이(가) {{ value | translate }}입니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -0,0 +1,13 @@
# Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Degrees'''.ko=온도
'''Temperature Offset'''.ko=온도 직접 설정
'''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''battery'''.ko=배터리
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }} 배터리가 {{ value }}였습니다
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }}가 움직임을 감지하였습니다.
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }} 동작이 중단되었습니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -14,6 +14,8 @@
*
*/
//DEPRECATED - Using the smartsense-motion-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "SmartSense Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Motion Sensor"
@@ -25,10 +27,6 @@ metadata {
command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326"
}
simulator {
@@ -233,7 +231,7 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (rawValue == 0) {}
if (rawValue == 0 || rawValue == 255) {}
else {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."

View File

@@ -0,0 +1,20 @@
# Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Degrees'''.ko=온도
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
'''No'''.ko=아니요
'''Tap to set'''.ko=눌러서 설정
'''Temperature Offset'''.ko=온도 직접 설정
'''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
'''Yes'''.ko=
'''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다
'''{{ device.displayName }} status was opened'''.ko={{ device.displayName }}은(는) 열린 상태입니다
'''{{ device.displayName }} was active'''.ko={{ device.displayName }}이(가) 활성화되었습니다
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}이(가) 닫혔습니다
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}이(가) 비활성화되었습니다
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}이(가) 열렸습니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -72,15 +72,12 @@ metadata {
]
)
}
valueTile("3axis", "device.threeAxis", decoration: "flat", wordWrap: false, width: 2, height: 2) {
state("threeAxis", label:'${currentValue}', unit:"", backgroundColor:"#ffffff")
}
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main(["contact", "acceleration", "temperature"])
details(["contact", "acceleration", "temperature", "3axis", "battery"])
details(["contact", "acceleration", "temperature", "battery"])
}
}

View File

@@ -13,6 +13,7 @@
* for the specific language governing permissions and limitations under the License.
*
*/
//DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
metadata {
definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -23,8 +24,7 @@
capability "Refresh"
capability "Temperature Measurement"
command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320"
fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321"
}
simulator {
@@ -225,7 +225,8 @@ def getTemperature(value) {
def volts = rawValue / 10
def descriptionText
if (volts > 3.5) {
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -220,7 +220,8 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (volts > 3.5) {
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -196,7 +196,8 @@ private Map getBatteryResult(rawValue) {
def volts = rawValue / 10
def descriptionText
if (volts > 3.5) {
if (rawValue == 0 || rawValue == 255) {}
else if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
}
else {

View File

@@ -23,22 +23,14 @@
capability "Battery"
capability "Configuration"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019",
manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_5", deviceJoinName: "Kwikset 5-Button Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019",
manufacturer: "Kwikset", model: "SMARTCODE_LEVER_5", deviceJoinName: "Kwikset 5-Button Lever"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019",
manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_10", deviceJoinName: "Kwikset 10-Button Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019",
manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_10T", deviceJoinName: "Kwikset 10-Button Touch Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019",
manufacturer: "Yale", model: "YRL220 TS LL", deviceJoinName: "Yale Touch Screen Lever Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019",
manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019",
manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019",
manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_5", deviceJoinName: "Kwikset 5-Button Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_LEVER_5", deviceJoinName: "Kwikset 5-Button Lever"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_10", deviceJoinName: "Kwikset 10-Button Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0004,0005,0009,0020,0101,0402,0B05,FDBD", outClusters: "000A,0019", manufacturer: "Kwikset", model: "SMARTCODE_DEADBOLT_10T", deviceJoinName: "Kwikset 10-Button Touch Deadbolt"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL220 TS LL", deviceJoinName: "Yale Touch Screen Lever Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD210 PB DB", deviceJoinName: "Yale Push Button Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRD220/240 TSDB", deviceJoinName: "Yale Touch Screen Deadbolt Lock"
fingerprint profileId: "0104", inClusters: "0000,0001,0003,0009,000A,0101,0020", outClusters: "000A,0019", manufacturer: "Yale", model: "YRL210 PB LL", deviceJoinName: "Yale Push Button Lever Lock"
}
tiles(scale: 2) {

View File

@@ -0,0 +1,404 @@
/**
* Garadget Connect
*
* Copyright 2016 Stuart Buchanan
*
* 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.
*
*/
import java.text.DecimalFormat
import groovy.json.JsonSlurper
import groovy.json.JsonOutput
private apiUrl() { "https://api.particle.io" }
private getVendorName() { "Garadget" }
private getVendorTokenPath(){ "https://api.particle.io/oauth/token" }
private getVendorIcon() { "https://dl.dropboxusercontent.com/s/lkrub180btbltm8/garadget_128.png" }
private getClientId() { appSettings.clientId }
private getClientSecret() { appSettings.clientSecret }
private getServerUrl() { if(!appSettings.serverUrl){return getApiServerUrl()} }
// Automatically generated. Make future change here.
definition(
name: "Garadget (Connect)",
namespace: "fuzzysb",
author: "Stuart Buchanan",
description: "Garadget Integration",
category: "SmartThings Labs",
iconUrl: "https://dl.dropboxusercontent.com/s/lkrub180btbltm8/garadget_128.png",
iconX2Url: "https://dl.dropboxusercontent.com/s/w8tvaedewwq56kr/garadget_256.png",
iconX3Url: "https://dl.dropboxusercontent.com/s/5hiec37e0y5py06/garadget_512.png",
oauth: true,
singleInstance: true
) {
appSetting "serverUrl"
}
preferences {
page(name: "startPage", title: "Garadget Integration", content: "startPage", install: false)
page(name: "Credentials", title: "Fetch OAuth2 Credentials", content: "authPage", install: false)
page(name: "mainPage", title: "Garadget Integration", content: "mainPage")
page(name: "completePage", title: "${getVendorName()} is now connected to SmartThings!", content: "completePage")
page(name: "listDevices", title: "Garadget Devices", content: "listDevices", install: false)
page(name: "badCredentials", title: "Invalid Credentials", content: "badAuthPage", install: false)
}
mappings {
path("/receivedToken"){action: [POST: "receivedToken", GET: "receivedToken"]}
}
def startPage() {
if (state.garadgetAccessToken) { return mainPage() }
else { return authPage() }
}
def mainPage(){
def result = [success:false]
if (!state.garadgetAccessToken) {
createAccessToken()
log.debug "About to create Smarthings Garadget access token."
getToken(garadgetUsername, garadgetPassword)
}
if (state.garadgetAccessToken){
result.success = true
}
if(result.success == true) {
return completePage()
} else {
return badAuthPage()
}
}
def completePage(){
def description = "Tap 'Next' to proceed"
return dynamicPage(name: "completePage", title: "Credentials Accepted!", nextPage: listDevices , uninstall: true, install:false) {
section { href url: buildRedirectUrl("receivedToken"), style:"embedded", required:false, title:"${getVendorName()} is now connected to SmartThings!", description:description }
}
}
def badAuthPage(){
log.debug "In badAuthPage"
log.error "login result false"
return dynamicPage(name: "badCredentials", title: "Garadget", install:false, uninstall:true, nextPage: Credentials) {
section("") {
paragraph "Please check your username and password"
}
}
}
def authPage() {
log.debug "In authPage"
if(canInstallLabs()) {
def description = null
log.debug "Prompting for Auth Details."
description = "Tap to enter Credentials."
return dynamicPage(name: "Credentials", title: "Authorize Connection", nextPage: mainPage, uninstall: false , install:false) {
section("Generate Username and Password") {
input "garadgetUsername", "text", title: "Your Garadget Username", required: true
input "garadgetPassword", "password", title: "Your Garadget Password", required: true
}
}
}
else
{
def upgradeNeeded = """To use SmartThings Labs, your Hub should be completely up to date.
To update your Hub, access Location Settings in the Main Menu (tap the gear next to your location name), select your Hub, and choose "Update Hub"."""
return dynamicPage(name:"Credentials", title:"Upgrade needed!", nextPage:"", install:false, uninstall: true) {
section {
paragraph "$upgradeNeeded"
}
}
}
}
def createChildDevice(deviceFile, dni, name, label) {
log.debug "In createChildDevice"
try{
def childDevice = addChildDevice("fuzzysb", deviceFile, dni, null, [name: name, label: label, completedSetup: true])
} catch (e) {
log.error "Error creating device: ${e}"
}
}
def listDevices() {
log.debug "In listDevices"
def options = getDeviceList()
dynamicPage(name: "listDevices", title: "Choose devices", install: true) {
section("Devices") {
input "devices", "enum", title: "Select Device(s)", required: false, multiple: true, options: options, submitOnChange: true
}
}
}
def buildRedirectUrl(endPoint) {
log.debug "In buildRedirectUrl"
log.debug("returning: " + getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}")
return getServerUrl() + "/api/token/${state.accessToken}/smartapps/installations/${app.id}/${endPoint}"
}
def receivedToken() {
log.debug "In receivedToken"
def html = """
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>${getVendorName()} Connection</title>
<style type="text/css">
* { box-sizing: border-box; }
@font-face {
font-family: 'Swiss 721 W01 Thin';
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot');
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.eot?#iefix') format('embedded-opentype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.woff') format('woff'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.ttf') format('truetype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-thin-webfont.svg#swis721_th_btthin') format('svg');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'Swiss 721 W01 Light';
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot');
src: url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.eot?#iefix') format('embedded-opentype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.woff') format('woff'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.ttf') format('truetype'),
url('https://s3.amazonaws.com/smartapp-icons/Partner/fonts/swiss-721-light-webfont.svg#swis721_lt_btlight') format('svg');
font-weight: normal;
font-style: normal;
}
.container {
width: 560px;
padding: 40px;
/*background: #eee;*/
text-align: center;
}
img {
vertical-align: middle;
}
img:nth-child(2) {
margin: 0 30px;
}
p {
font-size: 2.2em;
font-family: 'Swiss 721 W01 Thin';
text-align: center;
color: #666666;
margin-bottom: 0;
}
/*
p:last-child {
margin-top: 0px;
}
*/
span {
font-family: 'Swiss 721 W01 Light';
}
</style>
</head>
<body>
<div class="container">
<img src=""" + getVendorIcon() + """ alt="Vendor icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/connected-device-icn%402x.png" alt="connected device icon" />
<img src="https://s3.amazonaws.com/smartapp-icons/Partner/support/st-logo%402x.png" alt="SmartThings logo" />
<p>Tap 'Done' to continue to Devices.</p>
</div>
</body>
</html>
"""
render contentType: 'text/html', data: html
}
def getDeviceList() {
def garadgetDevices = []
httpGet( apiUrl() + "/v1/devices?access_token=${state.garadgetAccessToken}"){ resp ->
def restDevices = resp.data
restDevices.each { garadget ->
if (garadget.connected == true)
garadgetDevices << ["${garadget.id}|${garadget.name}":"${garadget.name}"]
}
}
return garadgetDevices.sort()
}
def installed() {
log.debug "Installed with settings: ${settings}"
initialize()
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
unschedule()
initialize()
}
def uninstalled() {
log.debug "Uninstalling Garadget (Connect)"
deleteToken()
removeChildDevices(getChildDevices())
log.debug "Garadget (Connect) Uninstalled"
}
def initialize() {
log.debug "Initialized with settings: ${settings}"
// Pull the latest device info into state
getDeviceList();
def children = getChildDevices()
if(settings.devices) {
settings.devices.each { device ->
def item = device.tokenize('|')
def deviceId = item[0]
def deviceName = item[1]
def existingDevices = children.find{ d -> d.deviceNetworkId.contains(deviceId) }
if(!existingDevices) {
try {
createChildDevice("Garadget", deviceId + ":" + state.garadgetAccessToken, "${deviceName}", deviceName)
} catch (Exception e) {
log.error "Error creating device: ${e}"
}
}
}
}
// Do the initial poll
poll()
// Schedule it to run every 5 minutes
runEvery5Minutes("poll")
}
def getToken(garadgetUsername, garadgetPassword){
log.debug "Executing 'sendCommand.setState'"
def body = ("grant_type=password&username=${garadgetUsername}&password=${garadgetPassword}&expires_in=0")
sendCommand("createToken","particle","particle", body)
}
private sendCommand(method, user, pass, command) {
def userpassascii = "${user}:${pass}"
def userpass = "Basic " + userpassascii.encodeAsBase64().toString()
def headers = [:]
headers.put("Authorization", userpass)
def methods = [
'createToken': [
uri: getVendorTokenPath(),
requestContentType: "application/x-www-form-urlencoded",
headers: headers,
body: command
],
'deleteToken': [
uri: apiUrl() + "/v1/access_tokens/${state.garadgetAccessToken}",
requestContentType: "application/x-www-form-urlencoded",
headers: headers,
]
]
def request = methods.getAt(method)
log.debug "Http Params ("+request+")"
try{
if (method == "createToken"){
log.debug "Executing createToken 'sendCommand'"
httpPost(request) { resp ->
parseResponse(resp)
}
}else if (method == "deleteToken"){
log.debug "Executing deleteToken 'sendCommand'"
httpDelete(request) { resp ->
parseResponse(resp)
}
}else{
log.debug "Executing default HttpGet 'sendCommand'"
httpGet(request) { resp ->
parseResponse(resp)
}
}
} catch(Exception e){
log.debug("___exception: " + e)
}
}
private parseResponse(resp) {
log.debug("Executing parseResponse: "+resp.data)
log.debug("Output status: "+resp.status)
if(resp.status == 200) {
log.debug("Executing parseResponse.successTrue")
state.garadgetAccessToken = resp.data.access_token
log.debug("Access Token: "+ state.garadgetAccessToken)
state.garadgetRefreshToken = resp.data.refresh_token
log.debug("Refresh Token: "+ state.garadgetRefreshToken)
state.garadgetTokenExpires = resp.data.expires_in
log.debug("Token Expires: "+ state.garadgetTokenExpires)
log.debug "Created new Garadget token"
}else if(resp.status == 201){
log.debug("Something was created/updated")
}
}
def poll() {
log.debug "In Poll"
getDeviceList();
getAllChildDevices().each {
it.statusCommand()
}
}
private Boolean canInstallLabs() {
return hasAllHubsOver("000.011.00603")
}
private List getRealHubFirmwareVersions() {
return location.hubs*.firmwareVersionString.findAll { it }
}
private Boolean hasAllHubsOver(String desiredFirmware) {
return realHubFirmwareVersions.every { fw -> fw >= desiredFirmware }
}
void deleteToken() {
try{
sendCommand("deleteToken","${garadgetUsername}","${garadgetPassword}",[])
log.debug "Deleted the existing Garadget Access Token"
} catch (e) {log.debug "Couldn't delete Garadget Token, There was an error (${e}), moving on"}
}
private removeChildDevices(delete) {
try {
delete.each {
deleteChildDevice(it.deviceNetworkId)
log.info "Successfully Removed Child Device: ${it.displayName} (${it.deviceNetworkId})"
}
}
catch (e) { log.error "There was an error (${e}) when trying to delete the child device" }
}

View File

@@ -1,322 +1,322 @@
/**
* 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.
*
* Button Controller
*
* Author: SmartThings
* Date: 2014-5-21
*/
definition(
name: "Button Controller",
namespace: "smartthings",
author: "SmartThings",
description: "Control devices with buttons like the Aeon Labs Minimote",
category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps@2x.png"
)
preferences {
page(name: "selectButton")
page(name: "configureButton1")
page(name: "configureButton2")
page(name: "configureButton3")
page(name: "configureButton4")
page(name: "timeIntervalInput", title: "Only during a certain time") {
section {
input "starting", "time", title: "Starting", required: false
input "ending", "time", title: "Ending", required: false
}
}
}
def selectButton() {
dynamicPage(name: "selectButton", title: "First, select your button device", nextPage: "configureButton1", uninstall: configured()) {
section {
input "buttonDevice", "capability.button", title: "Button", multiple: false, required: true
}
section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
def timeLabel = timeIntervalLabel()
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
}
}
}
def configureButton1() {
dynamicPage(name: "configureButton1", title: "Now let's decide how to use the first button",
nextPage: "configureButton2", uninstall: configured(), getButtonSections(1))
}
def configureButton2() {
dynamicPage(name: "configureButton2", title: "If you have a second button, set it up here",
nextPage: "configureButton3", uninstall: configured(), getButtonSections(2))
}
def configureButton3() {
dynamicPage(name: "configureButton3", title: "If you have a third button, you can do even more here",
nextPage: "configureButton4", uninstall: configured(), getButtonSections(3))
}
def configureButton4() {
dynamicPage(name: "configureButton4", title: "If you have a fourth button, you rule, and can set it up here",
install: true, uninstall: true, getButtonSections(4))
}
def getButtonSections(buttonNumber) {
return {
section("Lights") {
input "lights_${buttonNumber}_pushed", "capability.switch", title: "Pushed", multiple: true, required: false
input "lights_${buttonNumber}_held", "capability.switch", title: "Held", multiple: true, required: false
}
section("Locks") {
input "locks_${buttonNumber}_pushed", "capability.lock", title: "Pushed", multiple: true, required: false
input "locks_${buttonNumber}_held", "capability.lock", title: "Held", multiple: true, required: false
}
section("Sonos") {
input "sonos_${buttonNumber}_pushed", "capability.musicPlayer", title: "Pushed", multiple: true, required: false
input "sonos_${buttonNumber}_held", "capability.musicPlayer", title: "Held", multiple: true, required: false
}
section("Modes") {
input "mode_${buttonNumber}_pushed", "mode", title: "Pushed", required: false
input "mode_${buttonNumber}_held", "mode", title: "Held", required: false
}
def phrases = location.helloHome?.getPhrases()*.label
if (phrases) {
section("Hello Home Actions") {
log.trace phrases
input "phrase_${buttonNumber}_pushed", "enum", title: "Pushed", required: false, options: phrases
input "phrase_${buttonNumber}_held", "enum", title: "Held", required: false, options: phrases
}
}
section("Sirens") {
input "sirens_${buttonNumber}_pushed","capability.alarm" ,title: "Pushed", multiple: true, required: false
input "sirens_${buttonNumber}_held", "capability.alarm", title: "Held", multiple: true, required: false
}
section("Custom Message") {
input "textMessage_${buttonNumber}", "text", title: "Message", required: false
}
section("Push Notifications") {
input "notifications_${buttonNumber}_pushed","bool" ,title: "Pushed", required: false, defaultValue: false
input "notifications_${buttonNumber}_held", "bool", title: "Held", required: false, defaultValue: false
}
section("Sms Notifications") {
input "phone_${buttonNumber}_pushed","phone" ,title: "Pushed", required: false
input "phone_${buttonNumber}_held", "phone", title: "Held", required: false
}
}
}
def installed() {
initialize()
}
def updated() {
unsubscribe()
initialize()
}
def initialize() {
subscribe(buttonDevice, "button", buttonEvent)
}
def configured() {
return buttonDevice || buttonConfigured(1) || buttonConfigured(2) || buttonConfigured(3) || buttonConfigured(4)
}
def buttonConfigured(idx) {
return settings["lights_$idx_pushed"] ||
settings["locks_$idx_pushed"] ||
settings["sonos_$idx_pushed"] ||
settings["mode_$idx_pushed"] ||
settings["notifications_$idx_pushed"] ||
settings["sirens_$idx_pushed"] ||
settings["notifications_$idx_pushed"] ||
settings["phone_$idx_pushed"]
}
def buttonEvent(evt){
if(allOk) {
def buttonNumber = evt.data // why doesn't jsonData work? always returning [:]
def value = evt.value
log.debug "buttonEvent: $evt.name = $evt.value ($evt.data)"
log.debug "button: $buttonNumber, value: $value"
def recentEvents = buttonDevice.eventsSince(new Date(now() - 3000)).findAll{it.value == evt.value && it.data == evt.data}
log.debug "Found ${recentEvents.size()?:0} events in past 3 seconds"
if(recentEvents.size <= 1){
switch(buttonNumber) {
case ~/.*1.*/:
executeHandlers(1, value)
break
case ~/.*2.*/:
executeHandlers(2, value)
break
case ~/.*3.*/:
executeHandlers(3, value)
break
case ~/.*4.*/:
executeHandlers(4, value)
break
}
} else {
log.debug "Found recent button press events for $buttonNumber with value $value"
}
}
}
def executeHandlers(buttonNumber, value) {
log.debug "executeHandlers: $buttonNumber - $value"
def lights = find('lights', buttonNumber, value)
if (lights != null) toggle(lights)
def locks = find('locks', buttonNumber, value)
if (locks != null) toggle(locks)
def sonos = find('sonos', buttonNumber, value)
if (sonos != null) toggle(sonos)
def mode = find('mode', buttonNumber, value)
if (mode != null) changeMode(mode)
def phrase = find('phrase', buttonNumber, value)
if (phrase != null) location.helloHome.execute(phrase)
def textMessage = findMsg('textMessage', buttonNumber)
def notifications = find('notifications', buttonNumber, value)
if (notifications?.toBoolean()) sendPush(textMessage ?: "Button $buttonNumber was pressed" )
def phone = find('phone', buttonNumber, value)
if (phone != null) sendSms(phone, textMessage ?:"Button $buttonNumber was pressed")
def sirens = find('sirens', buttonNumber, value)
if (sirens != null) toggle(sirens)
}
def find(type, buttonNumber, value) {
def preferenceName = type + "_" + buttonNumber + "_" + value
def pref = settings[preferenceName]
if(pref != null) {
log.debug "Found: $pref for $preferenceName"
}
return pref
}
def findMsg(type, buttonNumber) {
def preferenceName = type + "_" + buttonNumber
def pref = settings[preferenceName]
if(pref != null) {
log.debug "Found: $pref for $preferenceName"
}
return pref
}
def toggle(devices) {
log.debug "toggle: $devices = ${devices*.currentValue('switch')}"
if (devices*.currentValue('switch').contains('on')) {
devices.off()
}
else if (devices*.currentValue('switch').contains('off')) {
devices.on()
}
else if (devices*.currentValue('lock').contains('locked')) {
devices.unlock()
}
else if (devices*.currentValue('lock').contains('unlocked')) {
devices.lock()
}
else if (devices*.currentValue('alarm').contains('off')) {
devices.siren()
}
else {
devices.on()
}
}
def changeMode(mode) {
log.debug "changeMode: $mode, location.mode = $location.mode, location.modes = $location.modes"
if (location.mode != mode && location.modes?.find { it.name == mode }) {
setLocationMode(mode)
}
}
// execution filter methods
private getAllOk() {
modeOk && daysOk && timeOk
}
private getModeOk() {
def result = !modes || modes.contains(location.mode)
log.trace "modeOk = $result"
result
}
private getDaysOk() {
def result = true
if (days) {
def df = new java.text.SimpleDateFormat("EEEE")
if (location.timeZone) {
df.setTimeZone(location.timeZone)
}
else {
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
}
def day = df.format(new Date())
result = days.contains(day)
}
log.trace "daysOk = $result"
result
}
private getTimeOk() {
def result = true
if (starting && ending) {
def currTime = now()
def start = timeToday(starting).time
def stop = timeToday(ending).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
}
log.trace "timeOk = $result"
result
}
private hhmm(time, fmt = "h:mm a")
{
def t = timeToday(time, location.timeZone)
def f = new java.text.SimpleDateFormat(fmt)
f.setTimeZone(location.timeZone ?: timeZone(time))
f.format(t)
}
private hideOptionsSection() {
(starting || ending || days || modes) ? false : true
}
private timeIntervalLabel() {
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}
/**
* 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.
*
* Button Controller
*
* Author: SmartThings
* Date: 2014-5-21
*/
definition(
name: "Button Controller",
namespace: "smartthings",
author: "SmartThings",
description: "Control devices with buttons like the Aeon Labs Minimote",
category: "Convenience",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/MyApps/Cat-MyApps@2x.png"
)
preferences {
page(name: "selectButton")
page(name: "configureButton1")
page(name: "configureButton2")
page(name: "configureButton3")
page(name: "configureButton4")
page(name: "timeIntervalInput", title: "Only during a certain time") {
section {
input "starting", "time", title: "Starting", required: false
input "ending", "time", title: "Ending", required: false
}
}
}
def selectButton() {
dynamicPage(name: "selectButton", title: "First, select your button device", nextPage: "configureButton1", uninstall: configured()) {
section {
input "buttonDevice", "capability.button", title: "Button", multiple: false, required: true
}
section(title: "More options", hidden: hideOptionsSection(), hideable: true) {
def timeLabel = timeIntervalLabel()
href "timeIntervalInput", title: "Only during a certain time", description: timeLabel ?: "Tap to set", state: timeLabel ? "complete" : null
input "days", "enum", title: "Only on certain days of the week", multiple: true, required: false,
options: ["Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"]
input "modes", "mode", title: "Only when mode is", multiple: true, required: false
}
}
}
def configureButton1() {
dynamicPage(name: "configureButton1", title: "Now let's decide how to use the first button",
nextPage: "configureButton2", uninstall: configured(), getButtonSections(1))
}
def configureButton2() {
dynamicPage(name: "configureButton2", title: "If you have a second button, set it up here",
nextPage: "configureButton3", uninstall: configured(), getButtonSections(2))
}
def configureButton3() {
dynamicPage(name: "configureButton3", title: "If you have a third button, you can do even more here",
nextPage: "configureButton4", uninstall: configured(), getButtonSections(3))
}
def configureButton4() {
dynamicPage(name: "configureButton4", title: "If you have a fourth button, you rule, and can set it up here",
install: true, uninstall: true, getButtonSections(4))
}
def getButtonSections(buttonNumber) {
return {
section("Lights") {
input "lights_${buttonNumber}_pushed", "capability.switch", title: "Pushed", multiple: true, required: false
input "lights_${buttonNumber}_held", "capability.switch", title: "Held", multiple: true, required: false
}
section("Locks") {
input "locks_${buttonNumber}_pushed", "capability.lock", title: "Pushed", multiple: true, required: false
input "locks_${buttonNumber}_held", "capability.lock", title: "Held", multiple: true, required: false
}
section("Sonos") {
input "sonos_${buttonNumber}_pushed", "capability.musicPlayer", title: "Pushed", multiple: true, required: false
input "sonos_${buttonNumber}_held", "capability.musicPlayer", title: "Held", multiple: true, required: false
}
section("Modes") {
input "mode_${buttonNumber}_pushed", "mode", title: "Pushed", required: false
input "mode_${buttonNumber}_held", "mode", title: "Held", required: false
}
def phrases = location.helloHome?.getPhrases()*.label
if (phrases) {
section("Hello Home Actions") {
log.trace phrases
input "phrase_${buttonNumber}_pushed", "enum", title: "Pushed", required: false, options: phrases
input "phrase_${buttonNumber}_held", "enum", title: "Held", required: false, options: phrases
}
}
section("Sirens") {
input "sirens_${buttonNumber}_pushed","capability.alarm" ,title: "Pushed", multiple: true, required: false
input "sirens_${buttonNumber}_held", "capability.alarm", title: "Held", multiple: true, required: false
}
section("Custom Message") {
input "textMessage_${buttonNumber}", "text", title: "Message", required: false
}
section("Push Notifications") {
input "notifications_${buttonNumber}_pushed","bool" ,title: "Pushed", required: false, defaultValue: false
input "notifications_${buttonNumber}_held", "bool", title: "Held", required: false, defaultValue: false
}
section("Sms Notifications") {
input "phone_${buttonNumber}_pushed","phone" ,title: "Pushed", required: false
input "phone_${buttonNumber}_held", "phone", title: "Held", required: false
}
}
}
def installed() {
initialize()
}
def updated() {
unsubscribe()
initialize()
}
def initialize() {
subscribe(buttonDevice, "button", buttonEvent)
}
def configured() {
return buttonDevice || buttonConfigured(1) || buttonConfigured(2) || buttonConfigured(3) || buttonConfigured(4)
}
def buttonConfigured(idx) {
return settings["lights_$idx_pushed"] ||
settings["locks_$idx_pushed"] ||
settings["sonos_$idx_pushed"] ||
settings["mode_$idx_pushed"] ||
settings["notifications_$idx_pushed"] ||
settings["sirens_$idx_pushed"] ||
settings["notifications_$idx_pushed"] ||
settings["phone_$idx_pushed"]
}
def buttonEvent(evt){
if(allOk) {
def buttonNumber = evt.data // why doesn't jsonData work? always returning [:]
def value = evt.value
log.debug "buttonEvent: $evt.name = $evt.value ($evt.data)"
log.debug "button: $buttonNumber, value: $value"
def recentEvents = buttonDevice.eventsSince(new Date(now() - 3000)).findAll{it.value == evt.value && it.data == evt.data}
log.debug "Found ${recentEvents.size()?:0} events in past 3 seconds"
if(recentEvents.size <= 1){
switch(buttonNumber) {
case ~/.*1.*/:
executeHandlers(1, value)
break
case ~/.*2.*/:
executeHandlers(2, value)
break
case ~/.*3.*/:
executeHandlers(3, value)
break
case ~/.*4.*/:
executeHandlers(4, value)
break
}
} else {
log.debug "Found recent button press events for $buttonNumber with value $value"
}
}
}
def executeHandlers(buttonNumber, value) {
log.debug "executeHandlers: $buttonNumber - $value"
def lights = find('lights', buttonNumber, value)
if (lights != null) toggle(lights)
def locks = find('locks', buttonNumber, value)
if (locks != null) toggle(locks)
def sonos = find('sonos', buttonNumber, value)
if (sonos != null) toggle(sonos)
def mode = find('mode', buttonNumber, value)
if (mode != null) changeMode(mode)
def phrase = find('phrase', buttonNumber, value)
if (phrase != null) location.helloHome.execute(phrase)
def textMessage = findMsg('textMessage', buttonNumber)
def notifications = find('notifications', buttonNumber, value)
if (notifications?.toBoolean()) sendPush(textMessage ?: "Button $buttonNumber was pressed" )
def phone = find('phone', buttonNumber, value)
if (phone != null) sendSms(phone, textMessage ?:"Button $buttonNumber was pressed")
def sirens = find('sirens', buttonNumber, value)
if (sirens != null) toggle(sirens)
}
def find(type, buttonNumber, value) {
def preferenceName = type + "_" + buttonNumber + "_" + value
def pref = settings[preferenceName]
if(pref != null) {
log.debug "Found: $pref for $preferenceName"
}
return pref
}
def findMsg(type, buttonNumber) {
def preferenceName = type + "_" + buttonNumber
def pref = settings[preferenceName]
if(pref != null) {
log.debug "Found: $pref for $preferenceName"
}
return pref
}
def toggle(devices) {
log.debug "toggle: $devices = ${devices*.currentValue('switch')}"
if (devices*.currentValue('switch').contains('on')) {
devices.off()
}
else if (devices*.currentValue('switch').contains('off')) {
devices.on()
}
else if (devices*.currentValue('lock').contains('locked')) {
devices.unlock()
}
else if (devices*.currentValue('lock').contains('unlocked')) {
devices.lock()
}
else if (devices*.currentValue('alarm').contains('off')) {
devices.siren()
}
else {
devices.on()
}
}
def changeMode(mode) {
log.debug "changeMode: $mode, location.mode = $location.mode, location.modes = $location.modes"
if (location.mode != mode && location.modes?.find { it.name == mode }) {
setLocationMode(mode)
}
}
// execution filter methods
private getAllOk() {
modeOk && daysOk && timeOk
}
private getModeOk() {
def result = !modes || modes.contains(location.mode)
log.trace "modeOk = $result"
result
}
private getDaysOk() {
def result = true
if (days) {
def df = new java.text.SimpleDateFormat("EEEE")
if (location.timeZone) {
df.setTimeZone(location.timeZone)
}
else {
df.setTimeZone(TimeZone.getTimeZone("America/New_York"))
}
def day = df.format(new Date())
result = days.contains(day)
}
log.trace "daysOk = $result"
result
}
private getTimeOk() {
def result = true
if (starting && ending) {
def currTime = now()
def start = timeToday(starting).time
def stop = timeToday(ending).time
result = start < stop ? currTime >= start && currTime <= stop : currTime <= stop || currTime >= start
}
log.trace "timeOk = $result"
result
}
private hhmm(time, fmt = "h:mm a")
{
def t = timeToday(time, location.timeZone)
def f = new java.text.SimpleDateFormat(fmt)
f.setTimeZone(location.timeZone ?: timeZone(time))
f.format(t)
}
private hideOptionsSection() {
(starting || ending || days || modes) ? false : true
}
private timeIntervalLabel() {
(starting && ending) ? hhmm(starting) + "-" + hhmm(ending, "h:mm a z") : ""
}

View File

@@ -235,6 +235,7 @@ def connectionStatus(message, redirectUrl = null) {
def getEcobeeThermostats() {
log.debug "getting device list"
atomicState.remoteSensors = []
def requestBody = '{"selection":{"selectionType":"registered","selectionMatch":"","includeRuntime":true,"includeSensors":true}}'
@@ -251,7 +252,7 @@ def getEcobeeThermostats() {
if (resp.status == 200) {
resp.data.thermostatList.each { stat ->
atomicState.remoteSensors = stat.remoteSensors
atomicState.remoteSensors = atomicState.remoteSensors == null ? stat.remoteSensors : atomicState.remoteSensors << stat.remoteSensors
def dni = [app.id, stat.identifier].join('.')
stats[dni] = getThermostatDisplayName(stat)
}
@@ -273,11 +274,14 @@ def getEcobeeThermostats() {
Map sensorsDiscovered() {
def map = [:]
atomicState.remoteSensors.each {
if (it.type != "thermostat") {
def value = "${it?.name}"
def key = "ecobee_sensor-"+ it?.id + "-" + it?.code
map["${key}"] = value
log.info "list ${atomicState.remoteSensors}"
atomicState.remoteSensors.each { sensors ->
sensors.each {
if (it.type != "thermostat") {
def value = "${it?.name}"
def key = "ecobee_sensor-"+ it?.id + "-" + it?.code
map["${key}"] = value
}
}
}
atomicState.sensors = map
@@ -541,10 +545,15 @@ def updateSensorData() {
def occupancy = ""
it.capability.each {
if (it.type == "temperature") {
if (location.temperatureScale == "F") {
temperature = Math.round(it.value.toDouble() / 10)
if (it.value == "unknown") {
temperature = "--"
} else {
temperature = convertFtoC(it.value.toDouble() / 10)
if (location.temperatureScale == "F") {
temperature = Math.round(it.value.toDouble() / 10)
} else {
temperature = convertFtoC(it.value.toDouble() / 10)
}
}
} else if (it.type == "occupancy") {

View File

@@ -455,7 +455,7 @@ def locationHandler(evt) {
log.error "/description.xml returned a bridge that didn't exist"
}
}
} else if(headerString?.contains("json")) {
} else if(headerString?.contains("json") && isValidSource(parsedEvent.mac)) {
log.trace "description.xml response (application/json)"
def body = new groovy.json.JsonSlurper().parseText(parsedEvent.body)
if (body.success != null) {
@@ -494,6 +494,11 @@ def doDeviceSync(){
discoverBridges()
}
def isValidSource(macAddress) {
def vbridges = getVerifiedHueBridges()
return (vbridges?.find {"${it.value.mac}" == macAddress}) != null
}
/////////////////////////////////////
//CHILD DEVICE METHODS
/////////////////////////////////////