Compare commits

...

63 Commits

Author SHA1 Message Date
Cety
1a28216451 MSA-977: Aeon lab Home energy meter and Smart Switch DTH 2016-03-23 00:04:32 -05:00
tslagle13
03c2dec425 Merge pull request #667 from tslagle13/fixes-to-vacation-lighting-director
Update vacation-lighting-director.groovy
2016-03-22 17:38:42 -07:00
tslagle13
38d0ca6170 Update vacation-lighting-director.groovy
* Moved scheduling over to Cron and added time as a trigger. 
 * Cleaned up formatting and some typos.
 * Updated license.
 * Made people option optional
 * Added sttement to unschedule on mode change if people option is not selected
2016-03-22 13:00:49 -07:00
Duncan McKee
836dd608c6 Merge pull request #539 from SmartThingsCommunity/DEVFRWK-78
Fix First Alert smoke alarm check-in handling
2016-03-21 22:33:07 -04:00
Juan Pablo Risso
43e4db28eb Merge pull request #660 from juano2310/hue_discover
Added hubVerification()
2016-03-21 16:21:33 -04:00
juano2310
df421a51ac Added hubVerification()
If the hub exist as a thing parsing the response from description.xml
was lost. Now it is sent back to the parent were the device can be
marked as verified if the modelName starts with "Philips hue bridge"
2016-03-21 14:38:24 -04:00
Duncan McKee
3affdd21fc Merge pull request #540 from SmartThingsCommunity/DVCSMP-1438
Z-Wave Motion Sensor: fix MissingMethodException on NotificationReport
2016-03-21 12:21:30 -04:00
Vinay Rao
42865abc55 Merge pull request #651 from SmartThingsCommunity/staging
Rolling down changes in staging to master
2016-03-18 13:11:08 -07:00
Lars Finander
dc1c78391e Merge pull request #650 from SmartThingsCommunity/MSA-963-2
MSA-963: Vinli Home Connect
2016-03-17 11:24:02 -07:00
Daniel
e279172383 Modifying 'Vinli Home Connect' 2016-03-17 12:49:40 -05:00
Daniel
0deb26810d MSA-963: Vinli Home Connect allows users to control their Smartthings Devices with their Vinli connect vehicles. The Vinli device is an OBD dongle that can report when it leaves or enters geofences. A user can, for instance, set their doors to lock and lights to turn off when they leave proximity to their home. 2016-03-17 12:18:14 -05:00
Tom Manley
d4fb75cc47 Merge pull request #649 from tpmanley/bugfix/multi_configure
multi: Fix occasional error with threeAxis attribute reporting
2016-03-17 12:06:52 -05:00
Tom Manley
2276748a91 multi: Fix occasional error with threeAxis attribute reporting
Resolves:
     https://smartthings.atlassian.net/browse/DVCSMP-1623
2016-03-16 17:01:42 -05:00
static null
8d423e7c4b Merge pull request #648 from staticnull/INTL-414
INTL-414 Add device name translations to device properties files
2016-03-16 13:50:43 -05:00
staticnull
0dfbddee38 INTL-414 Add device name translations to device properties files 2016-03-16 13:27:55 -05:00
Dylan Bijnagte
40f88fa436 Merge pull request #647 from Bijnagte/INTL-353-fixes
INTL-353 revert non i18n changes
2016-03-16 11:31:19 -05:00
Dylan Bijnagte
c3c8bafef4 Merge pull request #644 from Bijnagte/INTL-289-fixes
INTL-289 revert white space and fingerprint removal changes
2016-03-16 11:06:35 -05:00
Dylan Bijnagte
1b9f758bd6 Merge pull request #646 from Bijnagte/INTL-290-fixes
INTL-290 revert non i18n changes
2016-03-16 10:17:24 -05:00
Dylan Bijnagte
ef1b04c08a Merge pull request #645 from Bijnagte/INTL-292-fixes
INTL-292 undo non i18n changes
2016-03-16 10:15:40 -05:00
dylanbijnagte
9cece36d69 INTL-353 revert non i18n changes 2016-03-16 09:21:24 -05:00
dylanbijnagte
be220e02b2 INTL-290 revert non i18n changes 2016-03-16 09:12:06 -05:00
dylanbijnagte
131cc7b016 INTL-292 undo non i18n changes 2016-03-16 08:23:22 -05:00
dylanbijnagte
dd1e76e95a INTL-289 revert white space and fingerprint removal changes 2016-03-16 08:12:02 -05:00
Amol Mundayoor
8aff9e78f6 Merge pull request #641 from juano2310/Hue_update
DVCSMP-1615 & DEVC-372
2016-03-15 17:04:43 -07:00
juano2310
6fbef3b297 DVCSMP-1615 & DEVC-372
DVCSMP-1615  - Fix exception if Hue, Saturation or Hex is sent to
setColor

DEVC-372 - Improves readability of Activity Feed
2016-03-15 16:19:53 -04:00
Yaima
0b5779528c Merge pull request #639 from Yaima/master
Removed log.info
2016-03-14 14:20:37 -07:00
Yaima Valdivia
63d25528ae Removed log.info 2016-03-14 14:20:09 -07:00
Luke Bredeson
d8fe639a51 Merge pull request #625 from lbredeso/wemo-missing-dni-method
EX-45: Wemo (Connect) device addition fails on update for old devices
2016-03-14 16:14:36 -05:00
Yaima
92fea16beb Merge pull request #638 from Yaima/master
Ecobee - mode/ fan mode
2016-03-14 12:30:42 -07:00
Yaima Valdivia
25ae1306c4 Ecobee - mode/ fan mode 2016-03-14 12:30:03 -07:00
Yaima
321389aee3 Merge pull request #637 from Yaima/master
Ecobee - set mode/ fan from smart app
2016-03-14 12:28:03 -07:00
Yaima Valdivia
106f09445b Ecobee - set mode/ fan from smart app
switchToFanMode(value)
switchToMode(value)
2016-03-14 12:20:36 -07:00
tslagle13
410e9f40cc Merge pull request #633 from tslagle13/tts-fixes
Fix text-to-speech feature
2016-03-14 11:08:45 -07:00
Rob Zienert
6f173981e4 Merge pull request #635 from robzienert/branch-name-improvement
MISC: specifying dev shards when master branch is being deployed
2016-03-14 11:17:10 -05:00
Rob Zienert
a94d8f2378 specifying dev shards when master branch is being deployed 2016-03-14 11:04:24 -05:00
tslagle13
1578c48440 Fix text-to-speech feature
Refactor case statement so custom message works. Remove comment from input selection.
2016-03-13 17:04:15 -07:00
Vinay Rao
2126d85f6e Merge pull request #631 from workingmonk/stage1_v1_multi_garage
[DVCSMP-1107] Prepping up v1 multi for garage door functionality
2016-03-11 15:03:03 -08:00
Yaima
9218b40e25 Merge pull request #624 from Yaima/master
Fixed Ecobee polling
2016-03-11 13:02:15 -08:00
Dylan Bijnagte
7c326ec7c3 Merge pull request #630 from Bijnagte/update-korean-dth
update korean translations
2016-03-11 14:45:20 -06:00
dylanbijnagte
74598ec943 update korean translations 2016-03-11 14:05:39 -06:00
Dylan Bijnagte
96a82797de Merge pull request #587 from twack/i18n-smartsense-moisture-sensor
INTL-292 i18n alignment for smartsense-moisture-sensor
2016-03-11 11:36:38 -06:00
Dylan Bijnagte
c8e4eef66b Merge pull request #588 from twack/i18n-smartsense-motion-sensor
INTL-289 i18n alignment for smartsense-motion-sensor
2016-03-11 11:32:50 -06:00
Dylan Bijnagte
5effd158fc Merge pull request #591 from twack/i18n-mobile-presence
INTL-387 i18n alignment for mobile-presense
2016-03-11 11:30:37 -06:00
Dylan Bijnagte
3a4b4d6345 Merge pull request #592 from twack/i18n-arrival-sensor-ha
INTL-359 Create i18n for arrival-sensor-ha
2016-03-11 11:29:58 -06:00
Dylan Bijnagte
27808c3996 Merge pull request #593 from twack/i18n-smartpower-outlet
INTL-353 create i18n korean translation for smartpower-outlet
2016-03-11 11:28:48 -06:00
Dylan Bijnagte
bd0d636b8c Merge pull request #589 from twack/i18n-smartsense-multi-sensor
INTL-290 i18n alignment for smartsense-multi-sensor
2016-03-11 11:27:40 -06:00
Vinay Rao
7526d2b445 Merge pull request #628 from SmartThingsCommunity/master
Rolling up master to staging
2016-03-10 18:35:42 -08:00
Vinay Rao
caf761c015 Merging changes from staging to master 2016-03-10 18:11:54 -08:00
Vinay Rao
fb9f1dee47 Merge pull request #626 from munds/hue-hotfix
Hue hotfix for color temperature and overall stability
2016-03-10 15:04:01 -08:00
Amol Mundayoor
3a433d3865 Hue hotfix for color temperature and overall stability 2016-03-10 14:21:49 -08:00
Luke Bredeson
fbb248dc31 EX-45: Wemo (Connect) device addition fails on update for old devices that lack MAC in device data 2016-03-10 13:58:44 -06:00
Yaima Valdivia
a53e506538 Fixed Ecobee polling
https://smartthings.atlassian.net/browse/DVCSMP-1511
Ecobee was polling once per device, creating multiple API calls. Now it
is polling once at the smartapp level every 5 minutes and sending the
response to each child device.
2016-03-10 11:52:47 -08:00
twack
f593f08c5e create i18n korean translation for smartpower-outlet 2016-03-06 09:53:00 -08:00
twack
2d7300d9e5 create i18n and align arrival-sensor-ha SA file 2016-03-06 09:23:42 -08:00
twack
f31c3bf5ee i18n alignment for mobile-presense 2016-03-06 05:35:09 -08:00
twack
0799722bdb i18n alignment for smartsense-multi-sensor 2016-03-05 23:45:30 -08:00
twack
7fdb99524e i18n alignment for smartsense-motion-sensor 2016-03-05 23:15:21 -08:00
twack
04941dfa21 i18n groovy alignment for smartsense-moisture-sensor 2016-03-05 22:23:08 -08:00
Vinay Rao
8f25ff4434 Merge pull request #570 from SmartThingsCommunity/master
Rolling up changes to staging from master
2016-03-01 12:01:26 -08:00
Vinay Rao
9b87d39fe8 Merge pull request #546 from SmartThingsCommunity/master
Rolling up changes from master to staging
2016-02-23 10:50:20 -08:00
Duncan McKee
e61be4ff9c DVCSMP-1438: Fix Z-Wave Motion handling of Notification Report 2016-02-22 14:55:58 -05:00
Duncan McKee
6123fbeea5 DEVFRWK-78 Fix First Alert smoke alarm check-in handling 2016-02-22 13:52:56 -05:00
Vinay Rao
376779598a Prepping up v1 multi for garage door functionality 2016-02-15 13:01:12 -08:00
30 changed files with 1284 additions and 539 deletions

View File

@@ -52,6 +52,9 @@ hipchatShareFile {
hipchatSendNotification {
String branch = project.hasProperty('branch') ? project.property('branch') : 'unknown'
message = "Began executable deploy of SmartThingsPublic(${branch})."
if (branch == 'master') {
message += ' (dev shards)'
}
color = branch == 'master' ? 'yellow' : 'red'
notify = true
}

View File

@@ -0,0 +1,143 @@
/**
* 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.
*
* Aeon Home Energy Meter
*
* Author: SmartThings
*
* Date: 2013-05-30
*/
metadata {
definition (name: "Aeon Home Energy Meter With PCode", namespace: "smartthings", author: "SmartThings") {
capability "Energy Meter"
capability "Power Meter"
capability "Configuration"
capability "Sensor"
attribute "ManufacturerCode", "string"
attribute "ProductCode", "string"
attribute "ProduceTypeCode", "string"
attribute "WirelessConfig", "string"
command "reset"
fingerprint deviceId: "0x2101", inClusters: " 0x70,0x31,0x72,0x86,0x32,0x80,0x85,0x60"
}
// simulator metadata
simulator {
for (int i = 0; i <= 10000; i += 1000) {
status "power ${i} W": new physicalgraph.zwave.Zwave().meterV1.meterReport(
scaledMeterValue: i, precision: 3, meterType: 4, scale: 2, size: 4).incomingMessage()
}
for (int i = 0; i <= 100; i += 10) {
status "energy ${i} kWh": new physicalgraph.zwave.Zwave().meterV1.meterReport(
scaledMeterValue: i, precision: 3, meterType: 0, scale: 0, size: 4).incomingMessage()
}
}
// tile definitions
tiles {
valueTile("power", "device.power", decoration: "flat") {
state "default", label:'${currentValue} W'
}
valueTile("energy", "device.energy", decoration: "flat") {
state "default", label:'${currentValue} kWh'
}
standardTile("reset", "device.energy", inactiveLabel: false, decoration: "flat") {
state "default", label:'reset kWh', action:"reset"
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
main (["power","energy"])
details(["power","energy", "reset","refresh", "configure"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x31: 1, 0x32: 1, 0x60: 3])
if (cmd) {
result = createEvent(zwaveEvent(cmd))
}
log.debug "Parse returned ${result?.descriptionText}"
return result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv1.MeterReport cmd) {
if (cmd.scale == 0) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
} else if (cmd.scale == 1) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
}
else {
[name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"]
}
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
[:]
}
def refresh() {
delayBetween([
zwave.meterV2.meterGet(scale: 0).format(),
zwave.meterV2.meterGet(scale: 2).format()
])
}
def reset() {
// No V1 available
return [
zwave.meterV2.meterReset().format(),
zwave.meterV2.meterGet(scale: 0).format()
]
}
def configure() {
def cmd = delayBetween([
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 4).format(), // combined power in watts
zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 900).format(), // every 5 min
zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 8).format(), // combined energy in kWh
zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 3600).format(), // every 5 min
zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0).format(), // no third report
zwave.configurationV1.configurationSet(parameterNumber: 113, size: 4, scaledConfigurationValue: 900).format() // every 5 min
])
log.debug cmd
cmd
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productCode = String.format("%04X", cmd.productId)
def produceTypeCode = String.format("%04X", cmd.productTypeId)
def wirelessConfig = "ZWA"
sendEvent(name: "ManufacturerCode", value: manufacturerCode)
sendEvent(name: "ProductCode", value: productCode)
sendEvent(name: "ProduceTypeCode", value: produceTypeCode)
sendEvent(name: "WirelessConfig", value: wirelessConfig)
result << createEvent(descriptionText: "$device.displayName", isStateChange: false)
return result
}

View File

@@ -0,0 +1,200 @@
/**
* 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: "Aeon Smart Switch GEN5", namespace: "smartthings", author: "SmartThings") {
capability "Power Meter"
capability "Energy Meter"
capability "Actuator"
capability "Switch"
capability "Configuration"
capability "Polling"
capability "Refresh"
capability "Sensor"
attribute "ManufacturerCode", "string"
attribute "ProductCode", "string"
attribute "ProduceTypeCode", "string"
attribute "WirelessConfig", "string"
command "reset"
fingerprint deviceId: "0x1001", inClusters: "0x25,0x32,0x27,0x2C,0x2B,0x70,0x85,0x56,0x72,0x86,0x5E,0x59,0x7A,0x73,0x98,0xEF,0x5A,0x82", outClusters: "0x82"
}
// simulator metadata
simulator {
status "on": "command: 2003, payload: FF"
status "off": "command: 2003, payload: 00"
for (int i = 0; i <= 100; i += 10) {
status "energy ${i} kWh": new physicalgraph.zwave.Zwave().meterV2.meterReport(
scaledMeterValue: i, precision: 3, meterType: 0, scale: 0, size: 4).incomingMessage()
}
// reply messages
reply "2001FF,delay 100,2502": "command: 2503, payload: FF"
reply "200100,delay 100,2502": "command: 2503, payload: 00"
}
// tile definitions
tiles {
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
state "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
}
valueTile("power", "device.power", decoration: "flat") {
state "default", label:'${currentValue} W'
}
valueTile("energy", "device.energy", decoration: "flat") {
state "default", label:'${currentValue} kWh'
}
standardTile("configure", "device.power", inactiveLabel: false, decoration: "flat") {
state "configure", label:'', action:"configuration.configure", icon:"st.secondary.configure"
}
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label:'', action:"refresh.refresh", icon:"st.secondary.refresh"
}
main "switch"
details(["switch","power","energy","refresh","configure"])
}
}
def parse(String description) {
def result = null
def cmd = zwave.parse(description, [0x20: 1, 0x32: 2])
if (cmd) {
log.debug cmd
result = createEvent(zwaveEvent(cmd))
}
return result
}
def zwaveEvent(physicalgraph.zwave.commands.meterv3.MeterReport cmd) {
log.debug "MeterReport ${cmd}"
if (cmd.scale == 0) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kWh"]
} else if (cmd.scale == 1) {
[name: "energy", value: cmd.scaledMeterValue, unit: "kVAh"]
}
else {
[name: "power", value: Math.round(cmd.scaledMeterValue), unit: "W"]
}
}
def zwaveEvent(physicalgraph.zwave.commands.basicv1.BasicReport cmd)
{
[
name: "switch", value: cmd.value ? "on" : "off", type: "physical"
]
}
def zwaveEvent(physicalgraph.zwave.commands.switchbinaryv1.SwitchBinaryReport cmd)
{
[
name: "switch", value: cmd.value ? "on" : "off", type: "digital"
]
}
def zwaveEvent(physicalgraph.zwave.Command cmd) {
// Handles all Z-Wave commands we aren't interested in
return createEvent(descriptionText: "$device.displayName: $cmd", displayed: false)
[:]
}
def on() {
delayBetween([
secure(zwave.basicV1.basicSet(value: 0xFF)),
secure(zwave.switchBinaryV1.switchBinaryGet())
])
}
def off() {
delayBetween([
secure(zwave.basicV1.basicSet(value: 0x00)),
secure(zwave.switchBinaryV1.switchBinaryGet())
])
}
def poll() {
delayBetween([
secure(zwave.switchBinaryV1.switchBinaryGet()),
secure(zwave.meterV2.meterGet())
])
}
def refresh() {
secure(zwave.switchBinaryV1.switchBinaryGet())
}
def reset() {
return [
secure(zwave.meterV2.meterReset()),
secure(zwave.meterV2.meterGet())
]
}
def configure() {
delayBetween([
zwave.manufacturerSpecificV2.manufacturerSpecificGet().format(),
secure(zwave.configurationV1.configurationSet(parameterNumber: 101, size: 4, scaledConfigurationValue: 8)), // energy in kWh
secure(zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: 300)), // every 1 min
secure(zwave.configurationV1.configurationSet(parameterNumber: 102, size: 4, scaledConfigurationValue: 4)),
secure(zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 900)),
secure(zwave.configurationV1.configurationSet(parameterNumber: 103, size: 4, scaledConfigurationValue: 0))
])
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
def result = []
def manufacturerCode = String.format("%04X", cmd.manufacturerId)
def productCode = String.format("%04X", cmd.productId)
def produceTypeCode = String.format("%04X", cmd.productTypeId)
def wirelessConfig = "ZWP"
sendEvent(name: "ManufacturerCode", value: manufacturerCode)
sendEvent(name: "ProductCode", value: productCode)
sendEvent(name: "ProduceTypeCode", value: produceTypeCode)
sendEvent(name: "WirelessConfig", value: wirelessConfig)
result << createEvent(descriptionText: "$device.displayName", isStateChange: false)
return result
}
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x20: 1, 0x85: 2, 0x70: 1])
log.debug "encapsulated: $encapsulatedCommand"
if (encapsulatedCommand) {
state.sec = 1
zwaveEvent(encapsulatedCommand)
}
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private secureSequence(commands, delay=200) {
delayBetween(commands.collect{ secure(it) }, delay)
}

View File

@@ -11,6 +11,17 @@
* for the specific language governing permissions and limitations under the License.
*
*/
/*
* Purpose: Arrival Sensor HA DTH File
*
* Filename: Arrival-Sensor-HA.src/Arrival-Sensor-HA.groovy
*
* Change History:
* 1. 20160115 TW - Update/Edit to support i18n translations
* 2. 20160121 TW - Update to V4 battery calcs, added pref's page title translations
*/
metadata {
definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") {
capability "Tone"
@@ -32,7 +43,7 @@ metadata {
])
}
section {
input "checkInterval", "enum", title: "Presence timeout (minutes)",
input "checkInterval", "enum", title: "Presence timeout (minutes)", description: "Tap to set",
defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
}
}
@@ -82,7 +93,6 @@ def parse(String description) {
private handleReportAttributeMessage(String description) {
def descMap = zigbee.parseDescriptionAsMap(description)
if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
handleBatteryEvent(Integer.parseInt(descMap.value, 16))
}
@@ -94,6 +104,7 @@ private handleReportAttributeMessage(String description) {
* @param volts Battery voltage in .1V increments
*/
private handleBatteryEvent(volts) {
def descriptionText
if (volts == 0 || volts == 255) {
log.debug "Ignoring invalid value for voltage (${volts/10}V)"
}
@@ -107,15 +118,17 @@ private handleBatteryEvent(volts) {
volts = minVolts
else if (volts > maxVolts)
volts = maxVolts
def pct = batteryMap[volts]
if (pct != null) {
def value = batteryMap[volts]
if (value != null) {
def linkText = getLinkText(device)
descriptionText = '{{ linkText }} battery was {{ value }}'
def eventMap = [
name: 'battery',
value: pct,
descriptionText: "${linkText} battery was ${pct}%"
value: value,
descriptionText: descriptionText,
translatable: true
]
log.debug "Creating battery event for voltage=${volts/10}V: ${eventMap}"
log.debug "Creating battery event for voltage=${volts/10}V: ${linkText} ${eventMap.name} is ${eventMap.value}%"
sendEvent(eventMap)
}
}
@@ -131,13 +144,19 @@ private handlePresenceEvent(present) {
stopTimer()
}
def linkText = getLinkText(device)
def descriptionText
if ( present )
descriptionText = "{{ linkText }} has arrived"
else
descriptionText = "{{ linkText }} has left"
def eventMap = [
name: "presence",
value: present ? "present" : "not present",
linkText: linkText,
descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}",
descriptionText: descriptionText,
translatable: true
]
log.debug "Creating presence event: ${eventMap}"
log.debug "Creating presence event: ${device.displayName} ${eventMap.name} is ${eventMap.value}"
sendEvent(eventMap)
}
@@ -158,4 +177,4 @@ def checkPresenceCallback() {
if (timeSinceLastCheckin >= theCheckInterval) {
handlePresenceEvent(false)
}
}
}

View File

@@ -0,0 +1,35 @@
#==============================================================================
# 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: Arrival Sensor HA i18n Translation File
#
# Filename: Arrival-Sensor-HA.src/i18n/messages.properties
#
# Change History:
# 1. 20160115 TW Initial release with informal Korean translation.
# 2. 20160121 TW Added def preference section titles.
#==============================================================================
# Korean (ko)
# Device Preferences
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Set Device Image'''.ko=기기 이미지 설정
'''Presence timeout (minutes)'''.ko=시간 초과. 스마트폰 위치 정보
'''Tap to set'''.ko=눌러서 설정
'''Arrival Sensor'''.ko=도착알림 센서
# Events / Notifications
'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}남아있는 배터리는 {{ value }}입니다.
'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다.
'''{{ linkText }} has left'''.ko={{ linkText }}집을 나갔습니다.
#==============================================================================

View File

@@ -20,7 +20,6 @@ metadata {
capability "Temperature Measurement"
capability "Motion Sensor"
capability "Refresh"
capability "Polling"
}
tiles {
@@ -68,6 +67,6 @@ def refresh() {
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
parent.pollChild(this)
parent.pollChild()
}

View File

@@ -20,7 +20,6 @@ metadata {
capability "Actuator"
capability "Thermostat"
capability "Temperature Measurement"
capability "Polling"
capability "Sensor"
capability "Refresh"
capability "Relative Humidity Measurement"
@@ -134,9 +133,7 @@ def refresh() {
void poll() {
log.debug "Executing 'poll' using parent SmartApp"
def results = parent.pollChild(this)
generateEvent(results) //parse received message from parent
parent.pollChild()
}
def generateEvent(Map results) {
@@ -382,12 +379,16 @@ def getDataByName(String name) {
state[name] ?: device.getDataValue(name)
}
def setThermostatMode(String value) {
log.debug "setThermostatMode({$value})"
def setThermostatMode(String mode) {
log.debug "setThermostatMode($mode)"
mode = mode.toLowerCase()
switchToMode(mode)
}
def setThermostatFanMode(String value) {
log.debug "setThermostatFanMode({$value})"
def setThermostatFanMode(String mode) {
log.debug "setThermostatFanMode($mode)"
mode = mode.toLowerCase()
switchToFanMode(mode)
}
def generateModeEvent(mode) {

View File

@@ -17,16 +17,13 @@ metadata {
tiles(scale: 2) {
multiAttributeTile(name:"rich-control"){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
tileAttribute ("", key: "PRIMARY_CONTROL") {
attributeState "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#F3C200"
}
}
tileAttribute ("serialNumber", key: "SECONDARY_CONTROL") {
attributeState "default", label:'SN: ${currentValue}'
}
}
standardTile("icon", "icon", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#FFFFFF"
}
}
}
valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) {
state "default", label:'SN: ${currentValue}'
}
@@ -34,7 +31,7 @@ metadata {
state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false
}
main (["icon"])
main (["rich-control"])
details(["rich-control", "networkAddress"])
}
}
@@ -75,6 +72,7 @@ def parse(description) {
}
else if (contentType?.contains("xml")) {
log.debug "HUE BRIDGE ALREADY PRESENT"
parent.hubVerification(device.hub.id, msg.body)
}
}
}

View File

@@ -3,6 +3,7 @@
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
@@ -27,10 +28,10 @@ metadata {
tiles (scale: 2){
multiAttributeTile(name:"rich-control", type: "lighting", width: 6, height: 4, 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"
attributeState "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#00A0DC", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#C6C7CC", nextState:"turningOn"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
@@ -43,16 +44,10 @@ metadata {
}
}
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("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
@@ -60,29 +55,12 @@ metadata {
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.switch", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
state "level", action:"switch level.setLevel"
}
valueTile("level", "device.level", inactiveLabel: false, decoration: "flat") {
state "level", label: 'Level ${currentValue}%'
}
controlTile("saturationSliderControl", "device.saturation", "slider", height: 1, width: 2, inactiveLabel: false) {
state "saturation", action:"color control.setSaturation"
}
valueTile("saturation", "device.saturation", inactiveLabel: false, decoration: "flat") {
state "saturation", label: 'Sat ${currentValue} '
}
controlTile("hueSliderControl", "device.hue", "slider", height: 1, width: 2, inactiveLabel: false) {
state "hue", action:"color control.setHue"
}
valueTile("hue", "device.hue", inactiveLabel: false, decoration: "flat") {
state "hue", label: 'Hue ${currentValue} '
}
main(["switch"])
main(["rich-control"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
}
}
@@ -127,34 +105,34 @@ void nextLevel() {
void setLevel(percent) {
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
sendEvent(name: "level", value: percent, descriptionText: "Level has changed to ${percent}%")
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
parent.setSaturation(this, percent)
sendEvent(name: "saturation", value: percent)
sendEvent(name: "saturation", value: percent, displayed: false)
}
void setHue(percent) {
log.debug "Executing 'setHue'"
parent.setHue(this, percent)
sendEvent(name: "hue", value: percent)
sendEvent(name: "hue", value: percent, displayed: false)
}
void setColor(value) {
log.debug "setColor: ${value}, $this"
parent.setColor(this, value)
if (value.hue) { sendEvent(name: "hue", value: value.hue)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
if (value.hue) { sendEvent(name: "hue", value: value.hue, displayed: false)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation, displayed: false)}
if (value.hex) { sendEvent(name: "color", value: value.hex)}
if (value.level) { sendEvent(name: "level", value: value.level)}
if (value.switch) { sendEvent(name: "switch", value: value.switch)}
if (value.level) { sendEvent(name: "level", value: value.level, descriptionText: "Level has changed to ${value.level}%")}
sendEvent(name: "switch", value: "on")
}
void reset() {
log.debug "Executing 'reset'"
def value = [level:100, hex:"#90C638", saturation:56, hue:23]
def value = [level:100, saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
}

View File

@@ -1,97 +1,89 @@
/**
* Hue Lux Bulb
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Temperature"
capability "Switch"
capability "Refresh"
capability "Sensor"
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}%'
}
}
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, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["switch"])
details(["rich-control", "colorTempSliderControl","refresh"])
}
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
void on() {
parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
parent.off(this)
sendEvent(name: "switch", value: "off")
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}
/**
* Hue Lux Bulb
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Switch"
capability "Refresh"
capability "Sensor"
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}%'
}
}
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, decoration: "flat", width: 2, height: 2) {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["rich-control"])
details(["rich-control", "refresh"])
}
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
void on() {
parent.on(this)
sendEvent(name: "switch", value: "on")
}
void off() {
parent.off(this)
sendEvent(name: "switch", value: "off")
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
parent.setLevel(this, percent)
sendEvent(name: "level", value: percent)
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}

View File

@@ -19,13 +19,14 @@
#
# Change History:
# 1. 20160205 TW Initial release with informal Korean translation.
# 2. 20160224 TW Updated with formal Korean translation.
#==============================================================================
# Korean (ko)
# Device Preferences
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Set Device Image'''.ko=디바이스 이미지 설정
'''Set Device Image'''.ko=기기 이미지 설정
# Events / Notifications
'''{{ linkText }} has left'''.ko={{ linkText }}님이 나갔습니다
'''{{ linkText }} has arrived'''.ko={{ linkText }}님이 도착했습니다
'''{{ linkText }} has left'''.ko={{ linkText }}집을 나갔습니다.
'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다.
'''present'''.ko=집안
'''not present'''.ko=부재중
'''not present'''.ko=외출

View File

@@ -1,16 +1,28 @@
/**
* Copyright 2015 SmartThings
/*
===============================================================================
* 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:
* 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.
* 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 DTH File
*
* Filename: mobile-presence.src/mobile-presence.groovy
*
* Change History:
* 1. 20160205 TW - Update/Edit to support i18n translations
===============================================================================
*/
metadata {
definition (name: "Mobile Presence", namespace: "smartthings", author: "SmartThings") {
capability "Presence Sensor"
@@ -41,6 +53,7 @@ def parse(String description) {
def isStateChange = isStateChange(device, name, value)
def results = [
translatable: true,
name: name,
value: value,
unit: null,
@@ -72,8 +85,8 @@ private String parseValue(String description) {
private parseDescriptionText(String linkText, String value, String description) {
switch(value) {
case "present": return "$linkText has arrived"
case "not present": return "$linkText has left"
case "present": return "{{ linkText }} has arrived"
case "not present": return "{{ linkText }} has left"
default: return value
}
}
@@ -84,4 +97,4 @@ private getState(String value) {
case "not present": return "left"
default: return value
}
}
}

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: SmartPower Outlet i18n Translation File
#
# Filename: SmartPower-Outlet.src/i18n/messages.properties
#
# Change History:
# 1. 20160116 TW Initial release with informal Korean translation.
#==============================================================================
# Korean (ko)
# Device Preferences
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Outlet'''.ko=플러그
# Events descriptionText
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다
#==============================================================================

View File

@@ -1,19 +1,26 @@
/**
* Copyright 2015 SmartThings
/*
===============================================================================
* 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:
* 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.
* 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: SmartPower Outlet DTH File
*
* SmartPower Outlet (CentraLite)
* Filename: SmartPower-Outlet.src/SmartPower-Outlet.groovy
*
* Author: SmartThings
* Date: 2015-08-23
* Change History:
* 1. 20160117 TW - Update/Edit to support i18n translations
===============================================================================
*/
metadata {
// Automatically generated. Make future change here.
@@ -91,22 +98,22 @@ def parse(String description) {
finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description))
if (finalResult) {
log.info finalResult
log.info "final result = $finalResult"
if (finalResult.type == "update") {
log.info "$device updates: ${finalResult.value}"
}
else if (finalResult.type == "power") {
def powerValue = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue)
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true )
/*
Dividing by 10 as the Divisor is 10000 and unit is kW for the device. AttrId: 0302 and 0300. Simplifying to 10
power level is an integer. The exact power level with correct units needs to be handled in the device type
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/
}
else {
sendEvent(name: finalResult.type, value: finalResult.value)
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off'
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
}
}
else {

View File

@@ -1,14 +1,41 @@
# 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=배터리
#==============================================================================
# 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: SmartSense Moisture Sensor i18n Translation File
#
# Filename: SmartSense-Moisture-Sensor.src/i18n/messages.properties
#
# Change History:
# 1. 20160116 TW Initial release with formal Korean translation.
#==============================================================================
# Korean (ko)
# Device Preferences
'''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 }}입니다
'''battery'''.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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Water Leak Sensor'''.ko=누수센서
# Events descriptionText
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
#==============================================================================

View File

@@ -1,18 +1,29 @@
/**
* SmartSense Moisture Sensor
/*
===============================================================================
* Copyright 2016 SmartThings
*
* Copyright 2014 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:
* 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.
* 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: SmartSense Moisture Sensor DTH File
*
* Filename: SmartSense-Moisture-Sensor.src/SmartSense-Moisture-Sensor.groovy
*
* Change History:
* 1. 20160116 TW - Update/Edit to support i18n translations
* 2. 20160125 TW = Incorporated new battery mapping from TM
===============================================================================
*/
metadata {
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") {
capability "Configuration"
@@ -43,7 +54,7 @@ metadata {
])
}
section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input title: "Temperature Offset", description: "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'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}
}
@@ -116,16 +127,16 @@ private Map parseCatchAllMessage(String description) {
resultMap = getBatteryResult(cluster.data.last())
break
case 0x0402:
// temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp)
resultMap = getTemperatureResult(value)
break
}
}
case 0x0402:
// temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp)
resultMap = getTemperatureResult(value)
break
}
}
return resultMap
return resultMap
}
private boolean shouldProcessMessage(cluster) {
@@ -220,7 +231,8 @@ private Map getBatteryResult(rawValue) {
def result = [
name: 'battery',
value: '--'
value: '--',
translatable: true
]
def volts = rawValue / 10
@@ -228,7 +240,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {}
else {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
}
else {
if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -245,7 +257,7 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
if (pct != null) {
result.value = pct
result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
else {
@@ -253,7 +265,7 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
}
@@ -263,27 +275,37 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) {
log.debug 'TEMP'
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset as int
def v = value as int
value = v + offset
}
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
def descriptionText
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [
name: 'temperature',
value: value,
descriptionText: descriptionText
descriptionText: descriptionText,
translatable: true
]
}
private Map getMoistureResult(value) {
log.debug 'water'
String descriptionText = "${device.displayName} is ${value}"
log.debug "water"
def descriptionText
if ( value == "wet" )
descriptionText = '{{ device.displayName }} is wet'
else
descriptionText = '{{ device.displayName }} is dry'
return [
name: 'water',
value: value,
descriptionText: descriptionText
descriptionText: descriptionText,
translatable: true
]
}

View File

@@ -1,13 +1,40 @@
# 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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
#==============================================================================
# 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: SmartSense Motion Sensor i18n Translation File
#
# Filename: SmartSense-Motion-Sensor.src/i18n/messages.properties
#
# Change History:
# 1. 20160116 TW Initial release with formal Korean translation.
# 2. 20160224 TW Updated formal Korean translations from Mike Stoller.
#==============================================================================
# Korean (ko)
# Device Preferences
'''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였습니다
'''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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Motion Sensor'''.ko=모션 센서
# Events descriptionText
'''{{ 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였습니다
'''{{ device.displayName }} battery has too much power: (> 3.5) volts.'''.ko={{ device.displayName }} 배터리 전력이 너무 높습니다(3.5볼트 초과).
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
#==============================================================================

View File

@@ -1,17 +1,27 @@
/**
* SmartSense Motion/Temp Sensor
/*
===============================================================================
* Copyright 2016 SmartThings
*
* Copyright 2014 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:
* 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.
* 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: SmartSense Motion Sensor DTH File
*
* Filename: SmartSense-Motion-Sensor.src/SmartSense-Motion-Sensor.groovy
*
* Change History:
* 1. 20160116 TW - Update/Edit to support i18n translations
* 2. 20160125 TW = Incorporated new battery mapping from TM
===============================================================================
*/
metadata {
@@ -47,7 +57,7 @@ metadata {
])
}
section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input title: "Temperature Offset", description: "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'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}
}
@@ -236,7 +246,8 @@ private Map getBatteryResult(rawValue) {
def result = [
name: 'battery',
value: '--'
value: '--',
translatable: true
]
def volts = rawValue / 10
@@ -244,7 +255,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {}
else {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
}
else {
if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -261,7 +272,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
if (pct != null) {
result.value = pct
result.descriptionText = "${linkText} battery was ${result.value}%"
def value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
else {
@@ -269,7 +281,7 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
}
@@ -279,28 +291,33 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) {
log.debug 'TEMP'
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset as int
def v = value as int
value = v + offset
}
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
def descriptionText
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [
name: 'temperature',
value: value,
descriptionText: descriptionText
descriptionText: descriptionText,
translatable: true
]
}
private Map getMotionResult(value) {
log.debug 'motion'
String linkText = getLinkText(device)
String descriptionText = value == 'active' ? "${linkText} detected motion" : "${linkText} motion has stopped"
String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped"
return [
name: 'motion',
value: value,
descriptionText: descriptionText
descriptionText: descriptionText,
translatable: true
]
}

View File

@@ -1,20 +1,45 @@
# 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=차고 문의 센서 사용 설정하기
#==============================================================================
# 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: SmartSense Multi Sensor i18n Translation File
#
# Filename: SmartSense-Multi-Sensor.src/i18n/messages.properties
#
# Change History:
# 1. 20160117 TW Initial release with informal Korean translation.
#==============================================================================
# Korean (ko)
# Device Preferences
'''Yes'''.ko=
'''No'''.ko=아니요
'''Tap to set'''.ko=눌러서 설정
'''battery'''.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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
'''Tap to set'''.ko=눌러서 설정
'''Give your device a name'''.ko=기기 이름 바꾸기
'''Multipurpose Sensor'''.ko=멀티 센서
# Events descriptionText
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다.
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다.
'''{{ device.displayName }} was active'''.ko={{ device.displayName }}활성화되었습니다.
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다.
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다.
'''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

@@ -1,17 +1,27 @@
/**
* SmartSense Multi
/*
===============================================================================
* Copyright 2016 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:
* 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.
* 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: SmartSense Multi Sensor DTH File
*
* Filename: SmartSense-Multi-Sensor.src/SmartSense-Multi-Sensor.groovy
*
* Change History:
* 1. 20160117 TW - Update/Edit to support i18n translations
* 2. 20160125 TW = Incorporated new battery mapping from TM
===============================================================================
*/
metadata {
@@ -62,11 +72,11 @@ metadata {
])
}
section {
input title: "Temperature Offset", description: "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\".", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input title: "Temperature Offset", description: "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'.", displayDuringSetup: false, type: "paragraph", element: "paragraph"
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}
section {
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
input("garageSensor", "enum", title: "Do you want to use this sensor on a garage door?", description: "Tap to set", options: ["Yes", "No"], defaultValue: "No", required: false, displayDuringSetup: false)
}
}
@@ -273,19 +283,19 @@ def updated() {
if (garageSensor == "Yes") {
def descriptionText = "Updating device to garage sensor"
if (device.latestValue("status") == "open") {
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText)
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true)
}
else if (device.latestValue("status") == "closed") {
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText)
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true)
}
}
else {
def descriptionText = "Updating device to open/close sensor"
if (device.latestValue("status") == "garage-open") {
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText)
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true)
}
else if (device.latestValue("status") == "garage-closed") {
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText)
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true)
}
}
}
@@ -301,11 +311,11 @@ def getTemperature(value) {
private Map getBatteryResult(rawValue) {
log.debug "Battery rawValue = ${rawValue}"
def linkText = getLinkText(device)
def result = [
name: 'battery',
value: '--'
value: '--',
translatable: true
]
def volts = rawValue / 10
@@ -313,7 +323,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {}
else {
if (volts > 3.5) {
result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
}
else {
if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -330,7 +340,7 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts]
if (pct != null) {
result.value = pct
result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
else {
@@ -338,7 +348,7 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
}
}
}
@@ -348,40 +358,50 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) {
log.debug "Temperature"
def linkText = getLinkText(device)
if (tempOffset) {
def offset = tempOffset as int
def v = value as int
value = v + offset
}
def descriptionText = "${linkText} was ${value}°${temperatureScale}"
def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C':
'{{ device.displayName }} was {{ value }}°F'
return [
name: 'temperature',
value: value,
descriptionText: descriptionText
descriptionText: descriptionText,
translatable: true
]
}
private Map getContactResult(value) {
log.debug "Contact"
def linkText = getLinkText(device)
def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}"
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed:false)
sendEvent(name: 'status', value: value, descriptionText: descriptionText)
log.debug "Contact: ${device.displayName} value = ${value}"
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed'
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true)
sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true)
}
private getAccelerationResult(numValue) {
log.debug "Acceleration"
def name = "acceleration"
def value = numValue.endsWith("1") ? "active" : "inactive"
def linkText = getLinkText(device)
def descriptionText = "$linkText was $value"
def value
def descriptionText
if ( numValue.endsWith("1") ) {
value = "active"
descriptionText = '{{ device.displayName }} was active'
} else {
value = "inactive"
descriptionText = '{{ device.displayName }} was inactive'
}
def isStateChange = isStateChange(device, name, value)
[
return [
name: name,
value: value,
descriptionText: descriptionText,
isStateChange: isStateChange
isStateChange: isStateChange,
translatable: true
]
}
@@ -451,15 +471,15 @@ def configure() {
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {0100}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {0100}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}", "delay 200",
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {0100}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
@@ -528,10 +548,9 @@ def garageEvent(zValue) {
garageValue = 'garage-open'
}
if (contactValue != null){
def linkText = getLinkText(device)
def descriptionText = "${linkText} was ${contactValue == 'open' ? 'opened' : 'closed'}"
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false)
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText)
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' :'{{ device.displayName }} was closed'
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false, translatable: true)
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true)
}
}

View File

@@ -22,6 +22,8 @@ metadata {
capability "Battery"
fingerprint profileId: "FC01", deviceId: "0139"
attribute "status", "string"
}
simulator {
@@ -99,7 +101,7 @@ def parse(String description) {
}
private Map parseSingleMessage(description) {
private List parseSingleMessage(description) {
def name = parseName(description)
def value = parseValue(description)
@@ -108,8 +110,9 @@ private Map parseSingleMessage(description) {
def handlerName = value == 'open' ? 'opened' : value
def isStateChange = isStateChange(device, name, value)
def results = [
name: name,
def results = []
results << createEvent(
name: "contact",
value: value,
unit: null,
linkText: linkText,
@@ -117,8 +120,18 @@ private Map parseSingleMessage(description) {
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
]
log.debug "Parse results for $device: $results"
)
results << createEvent(
name: "status",
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
)
results
}
@@ -269,7 +282,7 @@ private List parseRssiLqiMessage(String description) {
results
}
private getContactResult(part, description) {
private List getContactResult(part, description) {
def name = "contact"
def value = part.endsWith("1") ? "open" : "closed"
def handlerName = value == 'open' ? 'opened' : value
@@ -277,16 +290,30 @@ private getContactResult(part, description) {
def descriptionText = "$linkText was $handlerName"
def isStateChange = isStateChange(device, name, value)
[
name: name,
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
]
def results = []
results << createEvent(
name: "contact",
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed:false
)
results << createEvent(
name: "status",
value: value,
unit: null,
linkText: linkText,
descriptionText: descriptionText,
handlerName: handlerName,
isStateChange: isStateChange,
displayed: displayed(description, isStateChange)
)
results
}
private getAccelerationResult(part, description) {
@@ -449,6 +476,7 @@ private Boolean isOrientationMessage(String description) {
description ==~ /x:.*y:.*z:.*rssi:.*lqi:.*/
}
//Note: Not using this method anymore
private String parseName(String description) {
if (isSupportedDescription(description)) {
return "contact"

View File

@@ -57,7 +57,7 @@ def parse(String description) {
return result
}
def sensorValueEvent(Short value) {
def sensorValueEvent(value) {
if (value) {
createEvent(name: "motion", value: "active", descriptionText: "$device.displayName detected motion")
} else {
@@ -94,24 +94,24 @@ def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cm
{
def result = []
if (cmd.notificationType == 0x07) {
if (cmd.event == 0x01 || cmd.event == 0x02) {
if (cmd.v1AlarmType == 0x07) { // special case for nonstandard messages from Monoprice ensors
result << sensorValueEvent(cmd.v1AlarmLevel)
} else if (cmd.event == 0x01 || cmd.event == 0x02 || cmd.event == 0x07 || cmd.event == 0x08) {
result << sensorValueEvent(1)
} else if (cmd.event == 0x00) {
result << sensorValueEvent(0)
} else if (cmd.event == 0x03) {
result << createEvent(descriptionText: "$device.displayName covering was removed", isStateChange: true)
result << response(zwave.wakeUpV1.wakeUpIntervalSet(seconds:4*3600, nodeid:zwaveHubNodeId))
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
result << createEvent(name: "tamper", value: "detected", descriptionText: "$device.displayName covering was removed", isStateChange: true)
result << response(zwave.batteryV1.batteryGet())
} else if (cmd.event == 0x05 || cmd.event == 0x06) {
result << createEvent(descriptionText: "$device.displayName detected glass breakage", isStateChange: true)
} else if (cmd.event == 0x07) {
if(!state.MSR) result << response(zwave.manufacturerSpecificV2.manufacturerSpecificGet())
result << sensorValueEvent(1)
}
} else if (cmd.notificationType) {
def text = "Notification $cmd.notificationType: event ${([cmd.event] + cmd.eventParameter).join(", ")}"
result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, displayed: false)
result << createEvent(name: "notification$cmd.notificationType", value: "$cmd.event", descriptionText: text, isStateChange: true, displayed: false)
} else {
def value = cmd.v1AlarmLevel == 255 ? "active" : cmd.v1AlarmLevel ?: "inactive"
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, displayed: false)
result << createEvent(name: "alarm $cmd.v1AlarmType", value: value, isStateChange: true, displayed: false)
}
result
}

View File

@@ -61,37 +61,44 @@ def parse(String description) {
zwaveEvent(cmd, results)
}
}
// log.debug "\"$description\" parsed to ${results.inspect()}"
log.debug "'$description' parsed to ${results.inspect()}"
return results
}
def createSmokeOrCOEvents(name, results) {
def text = null
if (name == "smoke") {
text = "$device.displayName smoke was detected!"
// these are displayed:false because the composite event is the one we want to see in the app
results << createEvent(name: "smoke", value: "detected", descriptionText: text, displayed: false)
} else if (name == "carbonMonoxide") {
text = "$device.displayName carbon monoxide was detected!"
results << createEvent(name: "carbonMonoxide", value: "detected", descriptionText: text, displayed: false)
} else if (name == "tested") {
text = "$device.displayName was tested"
results << createEvent(name: "smoke", value: "tested", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "tested", descriptionText: text, displayed: false)
} else if (name == "smokeClear") {
text = "$device.displayName smoke is clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
name = "clear"
} else if (name == "carbonMonoxideClear") {
text = "$device.displayName carbon monoxide is clear"
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
name = "clear"
} else if (name == "testClear") {
text = "$device.displayName smoke is clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
name = "clear"
switch (name) {
case "smoke":
text = "$device.displayName smoke was detected!"
// these are displayed:false because the composite event is the one we want to see in the app
results << createEvent(name: "smoke", value: "detected", descriptionText: text, displayed: false)
break
case "carbonMonoxide":
text = "$device.displayName carbon monoxide was detected!"
results << createEvent(name: "carbonMonoxide", value: "detected", descriptionText: text, displayed: false)
break
case "tested":
text = "$device.displayName was tested"
results << createEvent(name: "smoke", value: "tested", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "tested", descriptionText: text, displayed: false)
break
case "smokeClear":
text = "$device.displayName smoke is clear"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
name = "clear"
break
case "carbonMonoxideClear":
text = "$device.displayName carbon monoxide is clear"
results << createEvent(name: "carbonMonoxide", value: "clear", descriptionText: text, displayed: false)
name = "clear"
break
case "testClear":
text = "$device.displayName test cleared"
results << createEvent(name: "smoke", value: "clear", descriptionText: text, displayed: false)
results << createEvent(name: "carbonMonoxide", value: "clear", displayed: false)
name = "clear"
break
}
// This composite event is used for updating the tile
results << createEvent(name: "alarmState", value: name, descriptionText: text)
@@ -117,8 +124,10 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
createSmokeOrCOEvents(cmd.alarmLevel ? "tested" : "testClear", results)
break
case 13: // sent every hour -- not sure what this means, just a wake up notification?
if (cmd.alarmLevel != 255) {
results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", displayed: true)
if (cmd.alarmLevel == 255) {
results << createEvent(descriptionText: "$device.displayName checked in", isStateChange: false)
} else {
results << createEvent(descriptionText: "$device.displayName code 13 is $cmd.alarmLevel", isStateChange:true, displayed:false)
}
// Clear smoke in case they pulled batteries and we missed the clear msg
@@ -127,9 +136,8 @@ def zwaveEvent(physicalgraph.zwave.commands.alarmv2.AlarmReport cmd, results) {
}
// Check battery if we don't have a recent battery event
def prevBattery = device.currentState("battery")
if (!prevBattery || (new Date().time - prevBattery.date.time)/60000 >= 60 * 53) {
results << new physicalgraph.device.HubAction(zwave.batteryV1.batteryGet().format())
if (!state.lastbatt || (now() - state.lastbatt) >= 48*60*60*1000) {
results << response(zwave.batteryV1.batteryGet())
}
break
default:
@@ -158,12 +166,17 @@ def zwaveEvent(physicalgraph.zwave.commands.sensoralarmv1.SensorAlarmReport cmd,
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv1.WakeUpNotification cmd, results) {
results << new physicalgraph.device.HubAction(zwave.wakeUpV1.wakeUpNoMoreInformation().format())
results << createEvent(descriptionText: "$device.displayName woke up", isStateChange: false)
if (!state.lastbatt || (now() - state.lastbatt) >= 56*60*60*1000) {
results << response(zwave.batteryV1.batteryGet(), "delay 2000", zwave.wakeUpV1.wakeUpNoMoreInformation())
} else {
results << response(zwave.wakeUpV1.wakeUpNoMoreInformation())
}
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd, results) {
def map = [ name: "battery", unit: "%" ]
def map = [ name: "battery", unit: "%", isStateChange: true ]
state.lastbatt = now()
if (cmd.batteryLevel == 0xFF) {
map.value = 1
map.descriptionText = "$device.displayName battery is low!"

View File

@@ -67,11 +67,11 @@ mappings {
def listAllDevices() {
def resp = []
switches.each {
resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub.name]
resp << [name: it.name, label: it.label, value: it.currentValue("switch"), type: "switch", id: it.id, hub: it.hub?.name]
}
locks.each {
resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub.name]
resp << [name: it.name, label: it.label, value: it.currentValue("lock"), type: "lock", id: it.id, hub: it.hub?.name]
}
return resp
}

View File

@@ -463,29 +463,32 @@ def pollChildren(child = null) {
}
// Poll Child is invoked from the Child Device itself as part of the Poll Capability
def pollChild(child){
def pollChild(){
if (pollChildren(child)){
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId]
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
child.generateEvent(tData.data) //parse received message from parent
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
return null
def devices = getChildDevices()
if (pollChildren()){
devices.each { child ->
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId]
log.info "pollChild(child)>> data for ${child.device.deviceNetworkId} : ${tData.data}"
child.generateEvent(tData.data) //parse received message from parent
} else if(atomicState.thermostats[child.device.deviceNetworkId] == null) {
log.error "ERROR: Device connection removed? no data for ${child.device.deviceNetworkId}"
return null
}
}
}
} else {
log.info "ERROR: pollChildren(child) for ${child.device.deviceNetworkId} after polling"
log.info "ERROR: pollChildren()"
return null
}
}
void poll() {
def devices = getChildDevices()
devices.each {pollChild(it)}
pollChild()
}
def availableModes(child) {

View File

@@ -24,7 +24,7 @@ definition(
category: "SmartThings Labs",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Partner/hue.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Partner/hue@2x.png",
//singleInstance: true
singleInstance: true
)
preferences {
@@ -68,7 +68,7 @@ def bridgeDiscovery(params=[:])
}
//setup.xml request every 3 seconds except on discoveries
if(((bridgeRefreshCount % 1) == 0) && ((bridgeRefreshCount % 5) != 0)) {
if(((bridgeRefreshCount % 3) == 0) && ((bridgeRefreshCount % 5) != 0)) {
verifyHueBridges()
}
@@ -175,6 +175,7 @@ private discoverHueBulbs() {
}
private verifyHueBridge(String deviceNetworkId, String host) {
log.trace "Verify Hue Bridge $deviceNetworkId"
sendHubCommand(new physicalgraph.device.HubAction([
method: "GET",
path: "/description.xml",
@@ -602,6 +603,20 @@ def parse(childDevice, description) {
}
}
def hubVerification(bodytext) {
log.trace "Bridge sent back description.xml for verification"
def body = new XmlSlurper().parseText(bodytext)
if (body?.device?.modelName?.text().startsWith("Philips hue bridge")) {
def bridges = getHueBridges()
def bridge = bridges.find {it?.key?.contains(body?.device?.UDN?.text())}
if (bridge) {
bridge.value << [name:body?.device?.friendlyName?.text(), serialNumber:body?.device?.serialNumber?.text(), verified: true]
} else {
log.error "/description.xml returned a bridge that didn't exist"
}
}
}
def on(childDevice) {
log.debug "Executing 'on'"
put("lights/${getId(childDevice)}/state", [on: true])
@@ -643,21 +658,25 @@ def setColorTemperature(childDevice, huesettings) {
def setColor(childDevice, huesettings) {
log.debug "Executing 'setColor($huesettings)'"
def hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
def sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
def hue = null
def sat = null
def xy = null
if (huesettings.hex) {
xy = getHextoXY(huesettings.hex)
} else if (huesettings.hue && huesettings.saturation) {
hue = Math.min(Math.round(huesettings.hue * 65535 / 100), 65535)
sat = Math.min(Math.round(huesettings.saturation * 255 / 100), 255)
}
def alert = huesettings.alert ? huesettings.alert : "none"
def transition = huesettings.transition ? huesettings.transition : 4
def value = [sat: sat, hue: hue, alert: alert, transitiontime: transition]
def value = [xy: xy, sat: sat, hue: hue, alert: alert, transitiontime: transition, on: true]
if (huesettings.level != null) {
if (huesettings.level == 1) value.bri = 1 else value.bri = Math.min(Math.round(huesettings.level * 255 / 100), 255)
value.on = value.bri > 0
}
if (huesettings.switch) {
value.on = huesettings.switch == "on"
}
log.debug "sending command $value"
put("lights/${getId(childDevice)}/state", value)
}
@@ -743,6 +762,59 @@ private getBridgeIP() {
return host
}
private getHextoXY(String colorStr) {
// For the hue bulb the corners of the triangle are:
// -Red: 0.675, 0.322
// -Green: 0.4091, 0.518
// -Blue: 0.167, 0.04
def cred = Integer.valueOf( colorStr.substring( 1, 3 ), 16 )
def cgreen = Integer.valueOf( colorStr.substring( 3, 5 ), 16 )
def cblue = Integer.valueOf( colorStr.substring( 5, 7 ), 16 )
double[] normalizedToOne = new double[3];
normalizedToOne[0] = (cred / 255);
normalizedToOne[1] = (cgreen / 255);
normalizedToOne[2] = (cblue / 255);
float red, green, blue;
// Make red more vivid
if (normalizedToOne[0] > 0.04045) {
red = (float) Math.pow(
(normalizedToOne[0] + 0.055) / (1.0 + 0.055), 2.4);
} else {
red = (float) (normalizedToOne[0] / 12.92);
}
// Make green more vivid
if (normalizedToOne[1] > 0.04045) {
green = (float) Math.pow((normalizedToOne[1] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
green = (float) (normalizedToOne[1] / 12.92);
}
// Make blue more vivid
if (normalizedToOne[2] > 0.04045) {
blue = (float) Math.pow((normalizedToOne[2] + 0.055)
/ (1.0 + 0.055), 2.4);
} else {
blue = (float) (normalizedToOne[2] / 12.92);
}
float X = (float) (red * 0.649926 + green * 0.103455 + blue * 0.197109);
float Y = (float) (red * 0.234327 + green * 0.743075 + blue * 0.022598);
float Z = (float) (red * 0.0000000 + green * 0.053077 + blue * 1.035763);
float x = X / (X + Y + Z);
float y = Y / (X + Y + Z);
double[] xy = new double[2];
xy[0] = x;
xy[1] = y;
return xy;
}
private Integer convertHexToInt(hex) {
Integer.parseInt(hex,16)
}

View File

@@ -1,31 +1,31 @@
'''Acceleration Detected'''.ko=가속 감지
'''Arrival Of'''.ko=도착
'''Both Push and SMS?'''.ko=푸시 메시지와 SMS 모두 사용하시겠습니까?
'''Button Pushed'''.ko=버튼이 눌렸습니다
'''Contact Closes'''.ko=접점 닫힘
'''Contact Opens'''.ko=접점 열림
'''Departure Of'''.ko=출발
'''Acceleration Detected'''.ko=가속 감지되었을 때
'''Arrival Of'''.ko=도착했을 때
'''Both Push and SMS?'''.ko=푸시 알람과 SMS 모두 사용
'''Button Pushed'''.ko=버튼이 눌렸을 때
'''Contact Closes'''.ko=닫힘이 감지되었을 때
'''Contact Opens'''.ko=열림이 감지되었을 때
'''Departure Of'''.ko=출발할 때
'''Message Text'''.ko=문자 메시지
'''Minutes'''.ko=
'''Motion Here'''.ko=동작
'''Phone Number (for SMS, optional)'''.ko=휴대전화 번호(문자 메시지 - 옵션)
'''Minutes'''.ko=메시지 전송 간격(분)
'''Motion Here'''.ko=움직임이 감지되었을 때
'''Phone Number (for SMS, optional)'''.ko=전화번호 (옵션)
'''Receive notifications when anything happens in your home.'''.ko=집 안에 무슨 일이 일어나면 알림이 전송됩니다.
'''Smoke Detected'''.ko=연기가 감지되었습니다
'''Switch Turned Off'''.ko=스위치 꺼
'''Switch Turned On'''.ko=스위치 꺼짐
'''Choose one or more, when...'''.ko=다음의 경우 하나 이상 선택
'''Smoke Detected'''.ko=연기가 감지되었을 때
'''Switch Turned Off'''.ko=스위치졌을 때
'''Switch Turned On'''.ko=스위치가 켜졌을 때
'''Choose one or more, when...'''.ko=다음 상황 중 하나 이상 선택
'''Yes'''.ko=
'''No'''.ko=아니요
'''Send this message (optional, sends standard status message if not specified)'''.ko=메시지 전송(선택적, 지정되지 않 경우 표준 상태 메시지를 보냅니다)
'''Via a push notification and/or an SMS message'''.ko=푸시 알/또는 문자 메시지를 통해
'''Set for specific mode(s)'''.ko=특정 모드 설정
'''Send this message (optional, sends standard status message if not specified)'''.ko=메시지 작성 (작성하지 않 경우 디폴트 메시지 전송)
'''Via a push notification and/or an SMS message'''.ko=푸시 알 SMS 설정
'''Set for specific mode(s)'''.ko=특정 상태 설정
'''Tap to set'''.ko=눌러서 설정
'''Minimum time between messages (optional, defaults to every message)'''.ko=메시지작 간 최소 시간(선택 사항, 모든 메시지의 기본 설정)
'''If outside the US please make sure to enter the proper country code'''.ko=미국 이외 거주자는 적절한 국가 코드를 입력했는지 확인하십시오
'''Water Sensor Wet'''.ko=Water Sensor에서 물이 감지되었습니다
'''Minimum time between messages (optional, defaults to every message)'''.ko=메시지 전송 간격 설정
'''If outside the US please make sure to enter the proper country code'''.ko=미국 이외 국가에 거주한다면 국가 코드와 함께 입력하여 주세요.
'''Water Sensor Wet'''.ko=누수가 감지되었을 때
'''{{ triggerEvent.linkText }} has arrived at the {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}에 도착했습니다
'''{{ triggerEvent.linkText }} has arrived at {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}에 도착했습니다
'''{{ triggerEvent.linkText }} has left the {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}을(를) 떠났습니다
'''{{ triggerEvent.linkText }} has left {{ location.name }}'''.ko={{ triggerEvent.linkText }}님이 {{ location.name }}을(를) 떠났습니다
'''Assign a name'''.ko=이름
'''Choose Modes'''.ko=모드 선택
'''Assign a name'''.ko=이름
'''Choose Modes'''.ko=상태 선택

View File

@@ -76,7 +76,7 @@ def mainPage() {
}
section{
input "actionType", "enum", title: "Action?", required: true, defaultValue: "Bell 1", options: [
//"Custom Message",
"Custom Message",
"Bell 1",
"Bell 2",
"Dogs Barking",
@@ -89,7 +89,7 @@ def mainPage() {
"Someone is arriving",
"Piano",
"Lightsaber"]
//input "message","text",title:"Play this message", required:false, multiple: false
input "message","text",title:"Play this message", required:false, multiple: false
}
section {
input "sonos", "capability.musicPlayer", title: "On this Speaker player", required: true
@@ -408,13 +408,15 @@ private loadText() {
case "Lightsaber":
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/lightsaber.mp3", duration: "10"]
break;
default:
/*if (message) {
case "Custom Message":
if (message) {
state.sound = textToSpeech(message instanceof List ? message[0] : message) // not sure why this is (sometimes) needed)
}
else {
state.sound = textToSpeech("You selected the custom message option but did not enter a message in the $app.label Smart App")
}*/
}
break;
default:
state.sound = [uri: "http://s3.amazonaws.com/smartapp-media/sonos/bell1.mp3", duration: "10"]
break;
}

View File

@@ -234,7 +234,7 @@ def addSwitches() {
def d
if (selectedSwitch) {
d = getChildDevices()?.find {
it.dni == selectedSwitch.value.mac || it.device.getDataValue("mac") == selectedSwitch.value.mac
it.deviceNetworkId == selectedSwitch.value.mac || it.device.getDataValue("mac") == selectedSwitch.value.mac
}
}
@@ -265,7 +265,7 @@ def addMotions() {
def d
if (selectedMotion) {
d = getChildDevices()?.find {
it.dni == selectedMotion.value.mac || it.device.getDataValue("mac") == selectedMotion.value.mac
it.deviceNetworkId == selectedMotion.value.mac || it.device.getDataValue("mac") == selectedMotion.value.mac
}
}
@@ -296,7 +296,7 @@ def addLightSwitches() {
def d
if (selectedLightSwitch) {
d = getChildDevices()?.find {
it.dni == selectedLightSwitch.value.mac || it.device.getDataValue("mac") == selectedLightSwitch.value.mac
it.deviceNetworkId == selectedLightSwitch.value.mac || it.device.getDataValue("mac") == selectedLightSwitch.value.mac
}
}

View File

@@ -1,11 +1,17 @@
/**
* Vacation Lighting Director
*
* Version 2.4 - Added information paragraphs
* Version 2.5 - Moved scheduling over to Cron and added time as a trigger.
* Cleaned up formatting and some typos.
* Updated license.
* Made people option optional
* Added sttement to unschedule on mode change if people option is not selected
*
* Version 2.4 - Added information paragraphs
*
* Source code can be found here: https://github.com/tslagle13/SmartThings/blob/master/smartapps/tslagle13/vacation-lighting-director.groovy
*
* Copyright 2015 Tim Slagle
* Copyright 2016 Tim Slagle
*
* 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:
@@ -51,8 +57,7 @@ def pageSetup() {
return dynamicPage(pageProperties) {
section(""){
paragraph "This app can be used to make your home seem occupied anytime you are away from your home. " +
"Please use each othe the sections below to setup the different preferences to your liking. " +
"I recommend this app be used with at least two away modes. An example would be 'Away Day' 'and Away Night'. "
"Please use each of the the sections below to setup the different preferences to your liking. "
}
section("Setup Menu") {
href "Setup", title: "Setup", description: "", state:greyedOut()
@@ -70,7 +75,7 @@ def Setup() {
def newMode = [
name: "newMode",
type: "mode",
title: "Which?",
title: "Modes",
multiple: true,
required: true
]
@@ -96,14 +101,6 @@ def Setup() {
required: true,
]
def people = [
name: "people",
type: "capability.presenceSensor",
title: "If these people are home do not change light status",
required: true,
multiple: true
]
def pageName = "Setup"
def pageProperties = [
@@ -116,10 +113,11 @@ def Setup() {
section(""){
paragraph "In this section you need to setup the deatils of how you want your lighting to be affected while " +
paragraph "you are away. All of these settings are required in order for the simulator to run correctly."
"you are away. All of these settings are required in order for the simulator to run correctly."
}
section("Which mode change triggers the simulator? (This app will only run in selected mode(s))") {
input newMode
section("Simulator Triggers") {
input newMode
href "timeIntervalInput", title: "Times", description: timeIntervalLabel(), refreshAfterSelection:true
}
section("Light switches to turn on/off") {
input switches
@@ -130,9 +128,6 @@ def Setup() {
section("Number of active lights at any given time") {
input number_of_active_lights
}
section("People") {
input people
}
}
}
@@ -162,18 +157,29 @@ def Settings() {
title: "Settings",
nextPage: "pageSetup"
]
def people = [
name: "people",
type: "capability.presenceSensor",
title: "If these people are home do not change light status",
required: false,
multiple: true
]
return dynamicPage(pageProperties) {
section(""){
paragraph "In this section you can restrict how your simulator runs. For instance you can restrict on which days it will run " +
paragraph "as well as a delay for the simulator to start after it is in the correct mode. Delaying the simulator helps with false starts based on a incorrect mode change."
"as well as a delay for the simulator to start after it is in the correct mode. Delaying the simulator helps with false starts based on a incorrect mode change."
}
section("Delay to start simulator") {
input falseAlarmThreshold
}
section("People") {
paragraph "Not using this setting may cause some lights to remain on when you arrive home"
input people
}
section("More options") {
href "timeIntervalInput", title: "Only during a certain time", description: getTimeLabel(starting, ending), state: greyedOutTime(starting, ending), refreshAfterSelection:true
input days
}
}
@@ -181,9 +187,24 @@ def Settings() {
page(name: "timeIntervalInput", title: "Only during a certain time", refreshAfterSelection:true) {
section {
input "starting", "time", title: "Starting", required: false
input "ending", "time", title: "Ending", required: false
input "startTimeType", "enum", title: "Starting at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true
if (startTimeType in ["sunrise","sunset"]) {
input "startTimeOffset", "number", title: "Offset in minutes (+/-)", range: "*..*", required: false
}
else {
input "starting", "time", title: "Start time", required: false
}
}
section {
input "endTimeType", "enum", title: "Ending at", options: [["time": "A specific time"], ["sunrise": "Sunrise"], ["sunset": "Sunset"]], defaultValue: "time", submitOnChange: true
if (endTimeType in ["sunrise","sunset"]) {
input "endTimeOffset", "number", title: "Offset in minutes (+/-)", range: "*..*", required: false
}
else {
input "ending", "time", title: "End time", required: false
}
}
}
def installed() {
@@ -201,10 +222,13 @@ def initialize(){
if (newMode != null) {
subscribe(location, modeChangeHandler)
}
if (starting != null) {
schedule(starting, modeChangeHandler)
}
log.debug "Installed with settings: ${settings}"
}
def modeChangeHandler(evt) {
log.debug "Mode change to: ${evt.value}"
def delay = (falseAlarmThreshold != null && falseAlarmThreshold != "") ? falseAlarmThreshold * 60 : 2 * 60
runIn(delay, scheduleCheck)
}
@@ -212,48 +236,54 @@ def modeChangeHandler(evt) {
//Main logic to pick a random set of lights from the large set of lights to turn on and then turn the rest off
def scheduleCheck(evt) {
if(allOk){
log.debug("Running")
// turn off all the switches
switches.off()
// grab a random switch
def random = new Random()
def inactive_switches = switches
for (int i = 0 ; i < number_of_active_lights ; i++) {
// if there are no inactive switches to turn on then let's break
if (inactive_switches.size() == 0){
break
if(allOk){
log.debug("Running")
// turn off all the switches
switches.off()
// grab a random switch
def random = new Random()
def inactive_switches = switches
for (int i = 0 ; i < number_of_active_lights ; i++) {
// if there are no inactive switches to turn on then let's break
if (inactive_switches.size() == 0){
break
}
// grab a random switch and turn it on
def random_int = random.nextInt(inactive_switches.size())
inactive_switches[random_int].on()
// then remove that switch from the pool off switches that can be turned on
inactive_switches.remove(random_int)
}
// re-run again when the frequency demands it
schedule("0 0/${frequency_minutes} * 1/1 * ? *", scheduleCheck)
}
// grab a random switch and turn it on
def random_int = random.nextInt(inactive_switches.size())
inactive_switches[random_int].on()
// then remove that switch from the pool off switches that can be turned on
inactive_switches.remove(random_int)
}
// re-run again when the frequency demands it
runIn(frequency_minutes * 60, scheduleCheck)
}
//Check to see if mode is ok but not time/day. If mode is still ok, check again after frequency period.
else if (modeOk) {
log.debug("mode OK. Running again")
runIn(frequency_minutes * 60, scheduleCheck)
switches.off()
}
//if none is ok turn off frequency check and turn off lights.
else if(people){
//don't turn off lights if anyone is home
if(someoneIsHome()){
log.debug("Stopping Check for Light")
//Check to see if mode is ok but not time/day. If mode is still ok, check again after frequency period.
else if (modeOk) {
log.debug("mode OK. Running again")
switches.off()
}
//if none is ok turn off frequency check and turn off lights.
else {
if(people){
//don't turn off lights if anyone is home
if(someoneIsHome()){
log.debug("Stopping Check for Light")
unschedule()
}
else{
log.debug("Stopping Check for Light and turning off all lights")
switches.off()
unschedule()
}
}
else{
log.debug("Stopping Check for Light and turning off all lights")
switches.off()
else if (!modeOk) {
unschedule()
}
}
}
}
@@ -286,26 +316,6 @@ private getDaysOk() {
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
}
else if (starting){
result = currTime >= start
}
else if (ending){
result = currTime <= stop
}
log.trace "timeOk = $result"
result
}
private getHomeIsEmpty() {
def result = true
@@ -330,25 +340,59 @@ private getSomeoneIsHome() {
return result
}
//gets the label for time restriction. Label phrasing changes depending on if there is both start and stop times or just one start/stop time.
def getTimeLabel(starting, ending){
def timeLabel = "Tap to set"
if(starting && ending){
timeLabel = "Between" + " " + hhmm(starting) + " " + "and" + " " + hhmm(ending)
}
else if (starting) {
timeLabel = "Start at" + " " + hhmm(starting)
}
else if(ending){
timeLabel = "End at" + hhmm(ending)
}
timeLabel
private getTimeOk() {
def result = true
def start = timeWindowStart()
def stop = timeWindowStop()
if (start && stop && location.timeZone) {
result = timeOfDayIsBetween(start, stop, new Date(), location.timeZone)
}
log.trace "timeOk = $result"
result
}
private timeWindowStart() {
def result = null
if (startTimeType == "sunrise") {
result = location.currentState("sunriseTime")?.dateValue
if (result && startTimeOffset) {
result = new Date(result.time + Math.round(startTimeOffset * 60000))
}
}
else if (startTimeType == "sunset") {
result = location.currentState("sunsetTime")?.dateValue
if (result && startTimeOffset) {
result = new Date(result.time + Math.round(startTimeOffset * 60000))
}
}
else if (starting && location.timeZone) {
result = timeToday(starting, location.timeZone)
}
log.trace "timeWindowStart = ${result}"
result
}
private timeWindowStop() {
def result = null
if (endTimeType == "sunrise") {
result = location.currentState("sunriseTime")?.dateValue
if (result && endTimeOffset) {
result = new Date(result.time + Math.round(endTimeOffset * 60000))
}
}
else if (endTimeType == "sunset") {
result = location.currentState("sunsetTime")?.dateValue
if (result && endTimeOffset) {
result = new Date(result.time + Math.round(endTimeOffset * 60000))
}
}
else if (ending && location.timeZone) {
result = timeToday(ending, location.timeZone)
}
log.trace "timeWindowStop = ${result}"
result
}
//fomrats time to readable format for time label
private hhmm(time, fmt = "h:mm a")
{
def t = timeToday(time, location.timeZone)
@@ -357,6 +401,41 @@ private hhmm(time, fmt = "h:mm a")
f.format(t)
}
private timeIntervalLabel() {
def start = ""
switch (startTimeType) {
case "time":
if (ending) {
start += hhmm(starting)
}
break
case "sunrise":
case "sunset":
start += startTimeType[0].toUpperCase() + startTimeType[1..-1]
if (startTimeOffset) {
start += startTimeOffset > 0 ? "+${startTimeOffset} min" : "${startTimeOffset} min"
}
break
}
def finish = ""
switch (endTimeType) {
case "time":
if (ending) {
finish += hhmm(ending)
}
break
case "sunrise":
case "sunset":
finish += endTimeType[0].toUpperCase() + endTimeType[1..-1]
if (endTimeOffset) {
finish += endTimeOffset > 0 ? "+${endTimeOffset} min" : "${endTimeOffset} min"
}
break
}
start && finish ? "${start} to ${finish}" : ""
}
//sets complete/not complete for the setup section on the main dynamic page
def greyedOut(){
def result = ""
@@ -369,16 +448,7 @@ def greyedOut(){
//sets complete/not complete for the settings section on the main dynamic page
def greyedOutSettings(){
def result = ""
if (starting || ending || days || falseAlarmThreshold) {
result = "complete"
}
result
}
//sets complete/not complete for time restriction section in settings
def greyedOutTime(starting, ending){
def result = ""
if (starting || ending) {
if (people || days || falseAlarmThreshold ) {
result = "complete"
}
result