From 376779598aded8108f8966ac49f7547143b071d9 Mon Sep 17 00:00:00 2001 From: Vinay Rao Date: Mon, 15 Feb 2016 13:01:12 -0800 Subject: [PATCH 01/23] Prepping up v1 multi for garage door functionality --- .../smartsense-multi.groovy | 60 ++++++++++++++----- 1 file changed, 44 insertions(+), 16 deletions(-) diff --git a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy index a6d2d14..0450d09 100644 --- a/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy +++ b/devicetypes/smartthings/smartsense-multi.src/smartsense-multi.groovy @@ -22,6 +22,8 @@ metadata { capability "Battery" fingerprint profileId: "FC01", deviceId: "0139" + + attribute "status", "string" } simulator { @@ -102,7 +104,7 @@ def parse(String description) { } -private Map parseSingleMessage(description) { +private List parseSingleMessage(description) { def name = parseName(description) def value = parseValue(description) @@ -111,8 +113,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, @@ -120,8 +123,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 } @@ -272,7 +285,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 @@ -280,16 +293,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) { @@ -452,6 +479,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" From 04941dfa2123d2fc0c1a93deac20f65170a04208 Mon Sep 17 00:00:00 2001 From: twack Date: Sat, 5 Mar 2016 22:23:08 -0800 Subject: [PATCH 02/23] i18n groovy alignment for smartsense-moisture-sensor --- .../i18n/messages.properties | 46 +++- .../smartsense-moisture-sensor.groovy | 259 ++++++++++-------- 2 files changed, 176 insertions(+), 129 deletions(-) diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties index 307fbb8..aa32ea0 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties @@ -1,14 +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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다. -'''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=장치 이름을 지정 +# 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 }}% +#============================================================================== \ No newline at end of file diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index 1c38e23..a8034bb 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -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" @@ -20,18 +31,17 @@ metadata { capability "Refresh" capability "Temperature Measurement" capability "Water Sensor" - - command "enrollResponse" - + + command "enrollResponse" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor" } - + simulator { - + } preferences { @@ -43,11 +53,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 } } - + tiles(scale: 2) { multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ tileAttribute ("device.water", key: "PRIMARY_CONTROL") { @@ -56,7 +66,7 @@ metadata { } } valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { - state "temperature", label:'${currentValue}°', + state "temperature", label:'${currentValue}', backgroundColors:[ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], @@ -78,7 +88,7 @@ metadata { details(["water", "temperature", "battery", "refresh"]) } } - + def parse(String description) { log.debug "description: $description" @@ -92,59 +102,59 @@ def parse(String description) { else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } - else if (description?.startsWith('zone status')) { - map = parseIasMessage(description) - } - + else if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } + log.debug "Parse returned $map" def result = map ? createEvent(map) : null - - if (description?.startsWith('enroll request')) { - List cmds = enrollResponse() - log.debug "enroll response: ${cmds}" - result = cmds?.collect { new physicalgraph.device.HubAction(it) } - } - return result + + if (description?.startsWith('enroll request')) { + List cmds = enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result } - + private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break + Map resultMap = [:] + def cluster = zigbee.parse(description) + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + 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) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } - + private Map parseReportAttributeMessage(String description) { Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param -> def nameAndValue = param.split(":") map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] } log.debug "Desc Map: $descMap" - + Map resultMap = [:] if (descMap.cluster == "0402" && descMap.attrId == "0000") { def value = getTemperature(descMap.value) @@ -153,10 +163,10 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.cluster == "0001" && descMap.attrId == "0020") { resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) } - + return resultMap } - + private Map parseCustomMessage(String description) { Map resultMap = [:] if (description?.startsWith('temperature: ')) { @@ -167,42 +177,42 @@ private Map parseCustomMessage(String description) { } private Map parseIasMessage(String description) { - List parsedMsg = description.split(' ') - String msgCode = parsedMsg[2] + List parsedMsg = description.split(' ') + String msgCode = parsedMsg[2] + + Map resultMap = [:] + switch(msgCode) { + case '0x0020': // Closed/No Motion/Dry + resultMap = getMoistureResult('dry') + break - Map resultMap = [:] - switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry - resultMap = getMoistureResult('dry') - break + case '0x0021': // Open/Motion/Wet + resultMap = getMoistureResult('wet') + break - case '0x0021': // Open/Motion/Wet - resultMap = getMoistureResult('wet') - break + case '0x0022': // Tamper Alarm + break - case '0x0022': // Tamper Alarm - break + case '0x0023': // Battery Alarm + break - case '0x0023': // Battery Alarm - break + case '0x0024': // Supervision Report + log.debug 'dry with tamper alarm' + resultMap = getMoistureResult('dry') + break - case '0x0024': // Supervision Report - log.debug 'dry with tamper alarm' - resultMap = getMoistureResult('dry') - break + case '0x0025': // Restore Report + log.debug 'water with tamper alarm' + resultMap = getMoistureResult('wet') + break - case '0x0025': // Restore Report - log.debug 'water with tamper alarm' - resultMap = getMoistureResult('wet') - break + case '0x0026': // Trouble/Failure + break - case '0x0026': // Trouble/Failure - break - - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0028': // Test Mode + break + } + return resultMap } def getTemperature(value) { @@ -220,7 +230,8 @@ private Map getBatteryResult(rawValue) { def result = [ name: 'battery', - value: '--' + value: '--', + translatable: true ] def volts = rawValue / 10 @@ -228,10 +239,10 @@ 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") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -245,7 +256,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 { @@ -253,44 +265,53 @@ 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 }}" } } } return result } - 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 ] } def refresh() { log.debug "Refreshing Temperature and Battery" def refreshCmds = [ - "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", + "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200" ] @@ -300,32 +321,32 @@ def refresh() { def configure() { String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) log.debug "Configuring Reporting, IAS CIE, and Bindings." - def configCmds = [ + def configCmds = [ "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 1", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500", "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs - "send 0x${device.deviceNetworkId} 1 1", "delay 500", + "send 0x${device.deviceNetworkId} 1 1", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500", - "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", - "send 0x${device.deviceNetworkId} 1 1", "delay 500" + "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", + "send 0x${device.deviceNetworkId} 1 1", "delay 500" ] - return configCmds + refresh() // send refresh cmds as part of config + return configCmds + refresh() // send refresh cmds as part of config } def enrollResponse() { log.debug "Sending enroll response" String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) - [ + [ //Resending the CIE in case the enroll request is sent before CIE is written "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", //Enroll Response "raw 0x500 {01 23 00 00 00}", "send 0x${device.deviceNetworkId} 1 1", "delay 200" - ] + ] } private getEndpointId() { @@ -337,19 +358,19 @@ private hex(value) { } private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() + reverseArray(hex.decodeHex()).encodeHex() } private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array -} + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + return array +} \ No newline at end of file From 7fdb99524e1dd797e3866c7118c3fc3eedbfb05c Mon Sep 17 00:00:00 2001 From: twack Date: Sat, 5 Mar 2016 23:15:21 -0800 Subject: [PATCH 03/23] i18n alignment for smartsense-motion-sensor --- .../i18n/messages.properties | 42 ++- .../smartsense-motion-sensor.groovy | 257 ++++++++++-------- 2 files changed, 171 insertions(+), 128 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties index f1b7c52..c28062b 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties @@ -1,13 +1,39 @@ - -# 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 }}였습니다 +'''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=장치 이름을 지정 +# 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 }}였습니다 +#============================================================================== \ No newline at end of file diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index e787c24..fa76425 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -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 { @@ -19,18 +29,17 @@ metadata { capability "Motion Sensor" capability "Configuration" capability "Battery" - capability "Temperature Measurement" + capability "Temperature Measurement" capability "Refresh" - - command "enrollResponse" + + command "enrollResponse" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326-L", deviceJoinName: "Iris Motion Sensor" - fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326" + fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor" } simulator { @@ -47,7 +56,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 } } @@ -86,7 +95,7 @@ metadata { def parse(String description) { log.debug "description: $description" - + Map map = [:] if (description?.startsWith('catchall:')) { map = parseCatchAllMessage(description) @@ -97,55 +106,55 @@ def parse(String description) { else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } - else if (description?.startsWith('zone status')) { - map = parseIasMessage(description) - } - + else if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } + log.debug "Parse returned $map" def result = map ? createEvent(map) : null - - if (description?.startsWith('enroll request')) { - List cmds = enrollResponse() - log.debug "enroll response: ${cmds}" - result = cmds?.collect { new physicalgraph.device.HubAction(it) } - } - return result + + if (description?.startsWith('enroll request')) { + List cmds = enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result } private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break + Map resultMap = [:] + def cluster = zigbee.parse(description) + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + 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 case 0x0406: - log.debug 'motion' - resultMap.name = 'motion' - break - } - } + log.debug 'motion' + resultMap.name = 'motion' + break + } + } - return resultMap + return resultMap } private boolean shouldProcessMessage(cluster) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } private Map parseReportAttributeMessage(String description) { @@ -154,7 +163,7 @@ private Map parseReportAttributeMessage(String description) { map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] } log.debug "Desc Map: $descMap" - + Map resultMap = [:] if (descMap.cluster == "0402" && descMap.attrId == "0000") { def value = getTemperature(descMap.value) @@ -163,14 +172,14 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.cluster == "0001" && descMap.attrId == "0020") { resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) } - else if (descMap.cluster == "0406" && descMap.attrId == "0000") { - def value = descMap.value.endsWith("01") ? "active" : "inactive" - resultMap = getMotionResult(value) - } - + else if (descMap.cluster == "0406" && descMap.attrId == "0000") { + def value = descMap.value.endsWith("01") ? "active" : "inactive" + resultMap = getMotionResult(value) + } + return resultMap } - + private Map parseCustomMessage(String description) { Map resultMap = [:] if (description?.startsWith('temperature: ')) { @@ -181,44 +190,44 @@ private Map parseCustomMessage(String description) { } private Map parseIasMessage(String description) { - List parsedMsg = description.split(' ') - String msgCode = parsedMsg[2] + List parsedMsg = description.split(' ') + String msgCode = parsedMsg[2] + + Map resultMap = [:] + switch(msgCode) { + case '0x0020': // Closed/No Motion/Dry + resultMap = getMotionResult('inactive') + break - Map resultMap = [:] - switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry - resultMap = getMotionResult('inactive') - break + case '0x0021': // Open/Motion/Wet + resultMap = getMotionResult('active') + break - case '0x0021': // Open/Motion/Wet - resultMap = getMotionResult('active') - break + case '0x0022': // Tamper Alarm + log.debug 'motion with tamper alarm' + resultMap = getMotionResult('active') + break - case '0x0022': // Tamper Alarm - log.debug 'motion with tamper alarm' - resultMap = getMotionResult('active') - break + case '0x0023': // Battery Alarm + break - case '0x0023': // Battery Alarm - break + case '0x0024': // Supervision Report + log.debug 'no motion with tamper alarm' + resultMap = getMotionResult('inactive') + break - case '0x0024': // Supervision Report - log.debug 'no motion with tamper alarm' - resultMap = getMotionResult('inactive') - break + case '0x0025': // Restore Report + break - case '0x0025': // Restore Report - break + case '0x0026': // Trouble/Failure + log.debug 'motion with failure alarm' + resultMap = getMotionResult('active') + break - case '0x0026': // Trouble/Failure - log.debug 'motion with failure alarm' - resultMap = getMotionResult('active') - break - - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0028': // Test Mode + break + } + return resultMap } def getTemperature(value) { @@ -236,7 +245,8 @@ private Map getBatteryResult(rawValue) { def result = [ name: 'battery', - value: '--' + value: '--', + translatable: true ] def volts = rawValue / 10 @@ -244,10 +254,10 @@ 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") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -261,7 +271,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 +280,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,33 +290,39 @@ 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" + //String descriptionText = '{{ device.displayName }} is {{ value | translate }}' return [ name: 'motion', value: value, - descriptionText: descriptionText + descriptionText: descriptionText, + translatable: true ] } def refresh() { - log.debug "refresh called" + log.debug "refresh executed" def refreshCmds = [ "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200" @@ -355,19 +372,19 @@ private hex(value) { } private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() + reverseArray(hex.decodeHex()).encodeHex() } private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array -} + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + return array +} \ No newline at end of file From 0799722bdbd4a316447d92c3ed17eeeb75cc1e67 Mon Sep 17 00:00:00 2001 From: twack Date: Sat, 5 Mar 2016 23:45:30 -0800 Subject: [PATCH 04/23] i18n alignment for smartsense-multi-sensor --- .../i18n/messages.properties | 54 ++- .../smartsense-multi-sensor.groovy | 326 ++++++++++-------- 2 files changed, 226 insertions(+), 154 deletions(-) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties index 2326d40..a0fc58a 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties @@ -1,20 +1,46 @@ - -# 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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다. -'''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 }}이(가) 비활성화되었습니다 +'''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=장치 이름을 지정 +# 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=기기-열림/닫힘 센서 업데이트 중 +'''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다 +'''{{ device.displayName }} status was opened'''.ko={{ device.displayName }}은(는) 열린 상태입니다 diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 01df653..9bed55b 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -1,41 +1,50 @@ -/** - * 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 { - definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { - - capability "Three Axis" + metadata { + definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { + capability "Three Axis" capability "Battery" - capability "Configuration" - capability "Sensor" + capability "Configuration" + capability "Sensor" capability "Contact Sensor" capability "Acceleration Sensor" capability "Refresh" capability "Temperature Measurement" - + command "enrollResponse" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor" - fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor" + fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor" attribute "status", "string" - } + } - simulator { + simulator { status "open": "zone report :: type: 19 value: 0031" status "closed": "zone report :: type: 19 value: 0030" @@ -52,7 +61,7 @@ metadata { status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0" status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000" } - preferences { + preferences { section { image(name: 'educationalcontent', multiple: true, images: [ "http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg", @@ -62,13 +71,13 @@ 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?", translatable: true, description: "Tap to set", options: ["Yes","No"], defaultValue: "No", required: false, displayDuringSetup: false) } - } + } tiles(scale: 2) { multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){ @@ -100,16 +109,19 @@ metadata { ] ) } + valueTile("3axis", "device.threeAxis", decoration: "flat", wordWrap: false, width: 2, height: 2) { + state("threeAxis", label:'${currentValue}', unit:"", backgroundColor:"#ffffff") + } valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { state "battery", label:'${currentValue}% battery', unit:"" } - standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", action:"refresh.refresh", icon:"st.secondary.refresh" - } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action:"refresh.refresh", icon:"st.secondary.refresh" + } main(["status", "acceleration", "temperature"]) - details(["status", "acceleration", "temperature", "battery", "refresh"]) + details(["status", "acceleration", "temperature", "3axis", "battery", "refresh"]) } } @@ -118,61 +130,61 @@ def parse(String description) { if (description?.startsWith('catchall:')) { map = parseCatchAllMessage(description) } - else if (description?.startsWith('temperature: ')) { + else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } else if (description?.startsWith('zone status')) { map = parseIasMessage(description) } - def result = map ? createEvent(map) : null + def result = map ? createEvent(map) : null if (description?.startsWith('enroll request')) { List cmds = enrollResponse() log.debug "enroll response: ${cmds}" result = cmds?.collect { new physicalgraph.device.HubAction(it) } } - else if (description?.startsWith('read attr -')) { - result = parseReportAttributeMessage(description).each { createEvent(it) } - } + else if (description?.startsWith('read attr -')) { + result = parseReportAttributeMessage(description).each { createEvent(it) } + } return result } -private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - log.debug cluster - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break + private Map parseCatchAllMessage(String description) { + Map resultMap = [:] + def cluster = zigbee.parse(description) + log.debug cluster + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + resultMap = getBatteryResult(cluster.data.last()) + break - case 0xFC02: - log.debug 'ACCELERATION' - break + case 0xFC02: + log.debug 'ACCELERATION' + break - case 0x0402: - log.debug 'TEMP' - // 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: + log.debug 'TEMP' + // 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) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } private List parseReportAttributeMessage(String description) { @@ -199,12 +211,10 @@ private List parseReportAttributeMessage(String description) { result << parseAxis(threeAxisAttributes) descMap.value = descMap.value[-2..-1] } - result << getAccelerationResult(descMap.value) + result << getAccelerationResult(descMap.value) } else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) { - // The size is checked to ensure the attribute report contains X, Y and Z values - // If all three axis are not included then the attribute report is ignored - result << parseAxis(descMap.value) + result << parseAxis(descMap.value) } else if (descMap.cluster == "0001" && descMap.attrId == "0020") { result << getBatteryResult(Integer.parseInt(descMap.value, 16)) @@ -228,43 +238,43 @@ private Map parseIasMessage(String description) { Map resultMap = [:] switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry + case '0x0020': // Closed/No Motion/Dry if (garageSensor != "Yes"){ resultMap = getContactResult('closed') } - break + break - case '0x0021': // Open/Motion/Wet + case '0x0021': // Open/Motion/Wet if (garageSensor != "Yes"){ resultMap = getContactResult('open') } - break + break - case '0x0022': // Tamper Alarm - break + case '0x0022': // Tamper Alarm + break - case '0x0023': // Battery Alarm - break + case '0x0023': // Battery Alarm + break - case '0x0024': // Supervision Report + case '0x0024': // Supervision Report if (garageSensor != "Yes"){ resultMap = getContactResult('closed') } - break + break - case '0x0025': // Restore Report + case '0x0025': // Restore Report if (garageSensor != "Yes"){ resultMap = getContactResult('open') } - break + break - case '0x0026': // Trouble/Failure - break + case '0x0026': // Trouble/Failure + break - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0028': // Test Mode + break + } + return resultMap } def updated() { @@ -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) } } } @@ -305,7 +315,8 @@ private Map getBatteryResult(rawValue) { def result = [ name: 'battery', - value: '--' + value: '--', + translatable: true ] def volts = rawValue / 10 @@ -313,10 +324,10 @@ 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") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -330,7 +341,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 { @@ -338,7 +350,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 }}" } } } @@ -347,41 +359,71 @@ private Map getBatteryResult(rawValue) { } private Map getTemperatureResult(value) { - log.debug "Temperature" - def linkText = getLinkText(device) + log.debug 'TEMP' + def name = "temperature" 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 + name: name, + value: value, + 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) -} - -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 isStateChange = isStateChange(device, name, value) - [ + log.debug "Contact: ${device.displayName} value = ${value}" + def name = "contact" + + def descriptionText + if ( value == 'open' ) + descriptionText = '{{ device.displayName }} was opened' + else + descriptionText = '{{ device.displayName }} was closed' + + sendEvent(name: 'status', value: value, descriptionText: descriptionText, displayed: false) + def isStateChange = isStateChange(device, name, value) + return [ name: name, value: value, descriptionText: descriptionText, - isStateChange: isStateChange + isStateChange: isStateChange, + translatable: true + ] + +} + +private getAccelerationResult(numValue) { + log.debug "Acceleration is $value" + def name = "acceleration" + 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, + translatable: true ] } @@ -431,38 +473,37 @@ def refresh() { def configure() { String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) - log.debug "Configuring Reporting" - + log.debug "Configuring Device Reporting" + def configCmds = [ "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", "delay 200", //checkin time 6 hrs + "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "delay 200", + "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200", - "zcl mfg-code ${manufacturerCode}", "delay 200", - "zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "delay 200", + "zcl mfg-code ${manufacturerCode}", + "zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "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 mfg-code ${manufacturerCode}", + "zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}", "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 mfg-code ${manufacturerCode}", + "zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}", "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 mfg-code ${manufacturerCode}", + "zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" - ] - + ] return configCmds + refresh() } @@ -478,12 +519,17 @@ def enrollResponse() { "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", //Enroll Response - "raw 0x500 {01 23 00 00 00}", "delay 200", + "raw 0x500 {01 23 00 00 00}", "send 0x${device.deviceNetworkId} 1 1", "delay 200" ] } private Map parseAxis(String description) { + def hexToSignedInt = { hexVal -> + def unsignedVal = hexToInt(hexVal) + unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal + } + def z = hexToSignedInt(description[0..3]) def y = hexToSignedInt(description[10..13]) def x = hexToSignedInt(description[20..23]) @@ -510,11 +556,6 @@ private Map parseAxis(String description) { getXyzResult(xyzResults, description) } -private hexToSignedInt(hexVal) { - def unsignedVal = hexToInt(hexVal) - unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal -} - def garageEvent(zValue) { def absValue = zValue.abs() def contactValue = null @@ -529,9 +570,14 @@ def garageEvent(zValue) { } 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) + if ( contactValue == 'open' ) { + descriptionText: '{{ device.displayName }} was opened' + sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was opened', displayed:false, translatable: true) + sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was opened', translatable: true) + } else { + sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was closed', displayed:false, translatable: true) + sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was closed', translatable: true) + } } } @@ -586,4 +632,4 @@ private byte[] reverseArray(byte[] array) { i++; } return array -} +} \ No newline at end of file From f31c3bf5eef3fafaacdce8be2347a4018fe28fcc Mon Sep 17 00:00:00 2001 From: twack Date: Sun, 6 Mar 2016 05:35:09 -0800 Subject: [PATCH 05/23] i18n alignment for mobile-presense --- .../i18n/messages.properties | 3 +- .../mobile-presence.groovy | 33 +++++++++++++------ 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties b/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties index bfe484e..75313a5 100644 --- a/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties +++ b/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties @@ -19,6 +19,7 @@ # # Change History: # 1. 20160205 TW Initial release with informal Korean translation. +# 2. 20160224 TW Updated with formal Korean translation. #============================================================================== # Korean (ko) # Device Preferences @@ -28,4 +29,4 @@ '''{{ linkText }} has left'''.ko={{ linkText }}님이 나갔습니다 '''{{ linkText }} has arrived'''.ko={{ linkText }}님이 도착했습니다 '''present'''.ko=집안 -'''not present'''.ko=부재중 +'''not present'''.ko=부재중 \ No newline at end of file diff --git a/devicetypes/smartthings/mobile-presence.src/mobile-presence.groovy b/devicetypes/smartthings/mobile-presence.src/mobile-presence.groovy index 34bb78a..b0fa346 100644 --- a/devicetypes/smartthings/mobile-presence.src/mobile-presence.groovy +++ b/devicetypes/smartthings/mobile-presence.src/mobile-presence.groovy @@ -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 } -} +} \ No newline at end of file From 2d7300d9e52cf1a624766dfe7a27c01113eb24ec Mon Sep 17 00:00:00 2001 From: twack Date: Sun, 6 Mar 2016 09:23:42 -0800 Subject: [PATCH 06/23] create i18n and align arrival-sensor-ha SA file --- .../arrival-sensor-ha.groovy | 39 ++++++++++++++----- .../i18n/messages.properties | 35 +++++++++++++++++ 2 files changed, 64 insertions(+), 10 deletions(-) create mode 100644 devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties diff --git a/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy index d2f1660..bda1d20 100644 --- a/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy +++ b/devicetypes/smartthings/arrival-sensor-ha.src/arrival-sensor-ha.groovy @@ -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) } -} +} \ No newline at end of file diff --git a/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties b/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties new file mode 100644 index 0000000..0d4173c --- /dev/null +++ b/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties @@ -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 }} 남아있다 +#============================================================================== \ No newline at end of file From f593f08c5e6b434e7c9d8cff939b473c6f068b5d Mon Sep 17 00:00:00 2001 From: twack Date: Sun, 6 Mar 2016 09:53:00 -0800 Subject: [PATCH 07/23] create i18n korean translation for smartpower-outlet --- .../i18n/messages.properties | 30 +++++++++++++ .../smartpower-outlet.groovy | 42 ++++++++++++------- 2 files changed, 56 insertions(+), 16 deletions(-) create mode 100644 devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties diff --git a/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties new file mode 100644 index 0000000..5c18e1c --- /dev/null +++ b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties @@ -0,0 +1,30 @@ +#============================================================================== +# 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=장치 이름을 지정 +# 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 }} 와트 +#============================================================================== \ No newline at end of file diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index d6b3b76..ef2b6f4 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -1,20 +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: 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. definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") { @@ -91,22 +99,24 @@ 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) + def value = (finalResult.value as Integer)/10 + createEvent(name: "power", value: value, 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) + if ( finalResult.value == "on" ) + createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is On', translatable: true) + else + createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is Off', translatable: true) } } else { @@ -167,4 +177,4 @@ def getPowerDescription(descMap) { else { return [:] } -} +} \ No newline at end of file From a53e506538d8afdc77a23aeca08f618492710888 Mon Sep 17 00:00:00 2001 From: Yaima Valdivia Date: Thu, 10 Mar 2016 11:52:47 -0800 Subject: [PATCH 08/23] 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. --- .../ecobee-sensor.src/ecobee-sensor.groovy | 3 +- .../ecobee-thermostat.groovy | 5 +--- .../ecobee-connect.src/ecobee-connect.groovy | 30 +++++++++++-------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy b/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy index 6110a27..381f6fc 100644 --- a/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy +++ b/devicetypes/smartthings/ecobee-sensor.src/ecobee-sensor.groovy @@ -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() } diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index eb245d6..8c190b2 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -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) { diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy index d66e7c0..b077c43 100644 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy @@ -463,29 +463,33 @@ 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 -> + log.info "***found $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) { From fbb248dc317d53dcfb688f36970de12c9d1b9cb5 Mon Sep 17 00:00:00 2001 From: Luke Bredeson Date: Thu, 10 Mar 2016 13:58:44 -0600 Subject: [PATCH 09/23] EX-45: Wemo (Connect) device addition fails on update for old devices that lack MAC in device data --- smartapps/smartthings/wemo-connect.src/wemo-connect.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy index 6213f7b..af89807 100644 --- a/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy +++ b/smartapps/smartthings/wemo-connect.src/wemo-connect.groovy @@ -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 } } From 74598ec9431573e1753dd1e01b2d932655057965 Mon Sep 17 00:00:00 2001 From: dylanbijnagte Date: Fri, 11 Mar 2016 14:05:39 -0600 Subject: [PATCH 10/23] update korean translations --- .../i18n/messages.properties | 18 ++++---- .../i18n/messages.properties | 8 ++-- .../i18n/messages.properties | 10 ++--- .../i18n/messages.properties | 10 ++--- .../i18n/messages.properties | 12 ++--- .../i18n/messages.properties | 14 +++--- .../i18n/messages.properties | 44 +++++++++---------- 7 files changed, 58 insertions(+), 58 deletions(-) diff --git a/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties b/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties index 0d4173c..7bdabef 100644 --- a/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties +++ b/devicetypes/smartthings/arrival-sensor-ha.src/i18n/messages.properties @@ -23,13 +23,13 @@ #============================================================================== # 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=도착 센서 +'''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 }} 남아있다 -#============================================================================== \ No newline at end of file +'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}남아있는 배터리는 {{ value }}입니다. +'''{{ linkText }} has arrived'''.ko={{ linkText }}집에 도착했습니다. +'''{{ linkText }} has left'''.ko={{ linkText }}집을 나갔습니다. +#============================================================================== diff --git a/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties b/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties index 75313a5..3e4de71 100644 --- a/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties +++ b/devicetypes/smartthings/mobile-presence.src/i18n/messages.properties @@ -24,9 +24,9 @@ # 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=부재중 \ No newline at end of file +'''not present'''.ko=외출 diff --git a/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties index 5c18e1c..6645906 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties @@ -22,9 +22,9 @@ #============================================================================== # Korean (ko) # Device Preferences -'''Give your device a name'''.ko=장치 이름을 지정 +'''Give your device a name'''.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 }} 와트 -#============================================================================== \ No newline at end of file +'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다. +'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다. +'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전원은 {{ value }}와트입니다 +#============================================================================== diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties index aa32ea0..cacfbc8 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties @@ -29,12 +29,12 @@ '''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=장치 이름을 지정 +'''Give your device a name'''.ko=기기 이름 바꾸기 # Events descriptionText -'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}이(가) 마른 입니다 -'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}이(가) 젖은 입니다 +'''{{ 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 }}% -#============================================================================== \ No newline at end of file +'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다. +#============================================================================== diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties index c28062b..779e5a5 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties @@ -28,12 +28,12 @@ '''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=장치 이름을 지정 +'''Give your device a name'''.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 }} 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 }}였습니다 -#============================================================================== \ No newline at end of file +'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다. +#============================================================================== diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties index a0fc58a..bcf4fae 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties @@ -31,15 +31,15 @@ '''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=장치 이름을 지정 +'''Give your device a name'''.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 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 }}였습니다 +'''{{ device.displayName }} battery was {{ value }}'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}입니다. '''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중 '''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중 '''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다 diff --git a/smartapps/smartthings/notify-me-when.src/i18n/messages.properties b/smartapps/smartthings/notify-me-when.src/i18n/messages.properties index a190968..f5d69d6 100644 --- a/smartapps/smartthings/notify-me-when.src/i18n/messages.properties +++ b/smartapps/smartthings/notify-me-when.src/i18n/messages.properties @@ -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=상태 선택 From a94d8f2378e0fcd821f97888104afce3bd30c529 Mon Sep 17 00:00:00 2001 From: Rob Zienert Date: Mon, 14 Mar 2016 11:03:37 -0500 Subject: [PATCH 11/23] specifying dev shards when master branch is being deployed --- build.gradle | 3 +++ 1 file changed, 3 insertions(+) diff --git a/build.gradle b/build.gradle index 481d91a..8250fcb 100644 --- a/build.gradle +++ b/build.gradle @@ -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 } From 106f09445b7257f0ba0c6b3fa0035bde83b97dba Mon Sep 17 00:00:00 2001 From: Yaima Valdivia Date: Mon, 14 Mar 2016 12:20:36 -0700 Subject: [PATCH 12/23] Ecobee - set mode/ fan from smart app switchToFanMode(value) switchToMode(value) --- .../smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy | 2 ++ 1 file changed, 2 insertions(+) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index 8c190b2..d51b45a 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -381,10 +381,12 @@ def getDataByName(String name) { def setThermostatMode(String value) { log.debug "setThermostatMode({$value})" + switchToMode(value) } def setThermostatFanMode(String value) { log.debug "setThermostatFanMode({$value})" + switchToFanMode(value) } def generateModeEvent(mode) { From 25ae1306c40da7077aea4087b1e1c25d7ac500b9 Mon Sep 17 00:00:00 2001 From: Yaima Valdivia Date: Mon, 14 Mar 2016 12:30:03 -0700 Subject: [PATCH 13/23] Ecobee - mode/ fan mode --- .../ecobee-thermostat.src/ecobee-thermostat.groovy | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy index d51b45a..7b08b14 100644 --- a/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy +++ b/devicetypes/smartthings/ecobee-thermostat.src/ecobee-thermostat.groovy @@ -379,14 +379,16 @@ def getDataByName(String name) { state[name] ?: device.getDataValue(name) } -def setThermostatMode(String value) { - log.debug "setThermostatMode({$value})" - switchToMode(value) +def setThermostatMode(String mode) { + log.debug "setThermostatMode($mode)" + mode = mode.toLowerCase() + switchToMode(mode) } -def setThermostatFanMode(String value) { - log.debug "setThermostatFanMode({$value})" - switchToFanMode(value) +def setThermostatFanMode(String mode) { + log.debug "setThermostatFanMode($mode)" + mode = mode.toLowerCase() + switchToFanMode(mode) } def generateModeEvent(mode) { From 63d25528aed88fdcb6ef26f4f6c9d971ea21a336 Mon Sep 17 00:00:00 2001 From: Yaima Valdivia Date: Mon, 14 Mar 2016 14:20:09 -0700 Subject: [PATCH 14/23] Removed log.info --- smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy | 1 - 1 file changed, 1 deletion(-) diff --git a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy index b077c43..848b7b7 100644 --- a/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy +++ b/smartapps/smartthings/ecobee-connect.src/ecobee-connect.groovy @@ -469,7 +469,6 @@ def pollChild(){ if (pollChildren()){ devices.each { child -> - log.info "***found $child" if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){ if(atomicState.thermostats[child.device.deviceNetworkId] != null) { def tData = atomicState.thermostats[child.device.deviceNetworkId] From 6fbef3b297158c32dec393a9c33fd5ff7b31ab44 Mon Sep 17 00:00:00 2001 From: juano2310 Date: Tue, 15 Mar 2016 16:19:53 -0400 Subject: [PATCH 15/23] DVCSMP-1615 & DEVC-372 DVCSMP-1615 - Fix exception if Hue, Saturation or Hex is sent to setColor DEVC-372 - Improves readability of Activity Feed --- .../smartthings/hue-bulb.src/hue-bulb.groovy | 56 +++++--------- .../hue-lux-bulb.src/hue-lux-bulb.groovy | 9 +-- .../hue-connect.src/hue-connect.groovy | 73 +++++++++++++++++-- 3 files changed, 83 insertions(+), 55 deletions(-) diff --git a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy index f49ec95..a5d41ef 100644 --- a/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy +++ b/devicetypes/smartthings/hue-bulb.src/hue-bulb.groovy @@ -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() } diff --git a/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy b/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy index 49e7f2f..18affa4 100644 --- a/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy +++ b/devicetypes/smartthings/hue-lux-bulb.src/hue-lux-bulb.groovy @@ -36,13 +36,6 @@ 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("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { state "level", action:"switch level.setLevel" } @@ -51,7 +44,7 @@ metadata { state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" } - main(["switch"]) + main(["rich-control"]) details(["rich-control", "refresh"]) } } diff --git a/smartapps/smartthings/hue-connect.src/hue-connect.groovy b/smartapps/smartthings/hue-connect.src/hue-connect.groovy index 93453ac..5b908d8 100644 --- a/smartapps/smartthings/hue-connect.src/hue-connect.groovy +++ b/smartapps/smartthings/hue-connect.src/hue-connect.groovy @@ -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 { @@ -643,21 +643,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 +747,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) } From dd1e76e95aed0554dd71499566eb2a44be90cac4 Mon Sep 17 00:00:00 2001 From: dylanbijnagte Date: Wed, 16 Mar 2016 08:12:02 -0500 Subject: [PATCH 16/23] INTL-289 revert white space and fingerprint removal changes --- .../i18n/messages.properties | 14 +- .../smartsense-motion-sensor.groovy | 214 +++++++++--------- 2 files changed, 114 insertions(+), 114 deletions(-) diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties index 779e5a5..882827f 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties @@ -1,16 +1,16 @@ #============================================================================== # 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 +# 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 +# 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 @@ -35,5 +35,5 @@ '''{{ 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 }}입니다. +'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다. #============================================================================== diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy index fa76425..a840d48 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/smartsense-motion-sensor.groovy @@ -3,15 +3,15 @@ * 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 + * 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 + * 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 @@ -29,17 +29,18 @@ metadata { capability "Motion Sensor" capability "Configuration" capability "Battery" - capability "Temperature Measurement" + capability "Temperature Measurement" capability "Refresh" - - command "enrollResponse" + + command "enrollResponse" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305-S" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326" - fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325-S", deviceJoinName: "Motion Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3305" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3325" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3326-L", deviceJoinName: "Iris Motion Sensor" + fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor" } simulator { @@ -95,7 +96,7 @@ metadata { def parse(String description) { log.debug "description: $description" - + Map map = [:] if (description?.startsWith('catchall:')) { map = parseCatchAllMessage(description) @@ -106,55 +107,55 @@ def parse(String description) { else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } - else if (description?.startsWith('zone status')) { - map = parseIasMessage(description) - } - + else if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } + log.debug "Parse returned $map" def result = map ? createEvent(map) : null - - if (description?.startsWith('enroll request')) { - List cmds = enrollResponse() - log.debug "enroll response: ${cmds}" - result = cmds?.collect { new physicalgraph.device.HubAction(it) } - } - return result + + if (description?.startsWith('enroll request')) { + List cmds = enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result } private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break + Map resultMap = [:] + def cluster = zigbee.parse(description) + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + 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 case 0x0406: - log.debug 'motion' - resultMap.name = 'motion' - break - } - } + log.debug 'motion' + resultMap.name = 'motion' + break + } + } - return resultMap + return resultMap } private boolean shouldProcessMessage(cluster) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } private Map parseReportAttributeMessage(String description) { @@ -163,7 +164,7 @@ private Map parseReportAttributeMessage(String description) { map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] } log.debug "Desc Map: $descMap" - + Map resultMap = [:] if (descMap.cluster == "0402" && descMap.attrId == "0000") { def value = getTemperature(descMap.value) @@ -172,14 +173,14 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.cluster == "0001" && descMap.attrId == "0020") { resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) } - else if (descMap.cluster == "0406" && descMap.attrId == "0000") { - def value = descMap.value.endsWith("01") ? "active" : "inactive" - resultMap = getMotionResult(value) - } - + else if (descMap.cluster == "0406" && descMap.attrId == "0000") { + def value = descMap.value.endsWith("01") ? "active" : "inactive" + resultMap = getMotionResult(value) + } + return resultMap } - + private Map parseCustomMessage(String description) { Map resultMap = [:] if (description?.startsWith('temperature: ')) { @@ -190,44 +191,44 @@ private Map parseCustomMessage(String description) { } private Map parseIasMessage(String description) { - List parsedMsg = description.split(' ') - String msgCode = parsedMsg[2] - - Map resultMap = [:] - switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry - resultMap = getMotionResult('inactive') - break + List parsedMsg = description.split(' ') + String msgCode = parsedMsg[2] - case '0x0021': // Open/Motion/Wet - resultMap = getMotionResult('active') - break + Map resultMap = [:] + switch(msgCode) { + case '0x0020': // Closed/No Motion/Dry + resultMap = getMotionResult('inactive') + break - case '0x0022': // Tamper Alarm - log.debug 'motion with tamper alarm' - resultMap = getMotionResult('active') - break + case '0x0021': // Open/Motion/Wet + resultMap = getMotionResult('active') + break - case '0x0023': // Battery Alarm - break + case '0x0022': // Tamper Alarm + log.debug 'motion with tamper alarm' + resultMap = getMotionResult('active') + break - case '0x0024': // Supervision Report - log.debug 'no motion with tamper alarm' - resultMap = getMotionResult('inactive') - break + case '0x0023': // Battery Alarm + break - case '0x0025': // Restore Report - break + case '0x0024': // Supervision Report + log.debug 'no motion with tamper alarm' + resultMap = getMotionResult('inactive') + break - case '0x0026': // Trouble/Failure - log.debug 'motion with failure alarm' - resultMap = getMotionResult('active') - break + case '0x0025': // Restore Report + break - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0026': // Trouble/Failure + log.debug 'motion with failure alarm' + resultMap = getMotionResult('active') + break + + case '0x0028': // Test Mode + break + } + return resultMap } def getTemperature(value) { @@ -257,7 +258,7 @@ private Map getBatteryResult(rawValue) { result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." } else { - if (device.getDataValue("manufacturer") == "SmartThings") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -272,7 +273,7 @@ private Map getBatteryResult(rawValue) { if (pct != null) { result.value = pct def value = pct - result.descriptionText = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } else { @@ -280,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 = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } } @@ -312,7 +313,6 @@ private Map getTemperatureResult(value) { private Map getMotionResult(value) { log.debug 'motion' String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped" - //String descriptionText = '{{ device.displayName }} is {{ value | translate }}' return [ name: 'motion', value: value, @@ -322,7 +322,7 @@ private Map getMotionResult(value) { } def refresh() { - log.debug "refresh executed" + log.debug "refresh called" def refreshCmds = [ "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200" @@ -372,19 +372,19 @@ private hex(value) { } private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() + reverseArray(hex.decodeHex()).encodeHex() } private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array -} \ No newline at end of file + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + return array +} From 131cc7b016dce8fb7ca3954191329664f8c1635e Mon Sep 17 00:00:00 2001 From: dylanbijnagte Date: Wed, 16 Mar 2016 08:23:22 -0500 Subject: [PATCH 17/23] INTL-292 undo non i18n changes --- .../i18n/messages.properties | 14 +- .../smartsense-moisture-sensor.groovy | 199 +++++++++--------- 2 files changed, 107 insertions(+), 106 deletions(-) diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties index cacfbc8..fc0e2a0 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties @@ -1,16 +1,16 @@ #============================================================================== # 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 +# 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 +# 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 @@ -36,5 +36,5 @@ '''{{ 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 }}입니다. +'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다. #============================================================================== diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy index a8034bb..599ba97 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/smartsense-moisture-sensor.groovy @@ -3,15 +3,15 @@ * 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 + * 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 + * 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 @@ -31,17 +31,18 @@ metadata { capability "Refresh" capability "Temperature Measurement" capability "Water Sensor" - - command "enrollResponse" + + command "enrollResponse" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-S", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3315-Seu", deviceJoinName: "Water Leak Sensor" fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor" } - + simulator { - + } preferences { @@ -57,7 +58,7 @@ metadata { input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false } } - + tiles(scale: 2) { multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ tileAttribute ("device.water", key: "PRIMARY_CONTROL") { @@ -66,7 +67,7 @@ metadata { } } valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { - state "temperature", label:'${currentValue}', + state "temperature", label:'${currentValue}°', backgroundColors:[ [value: 31, color: "#153591"], [value: 44, color: "#1e9cbb"], @@ -88,7 +89,7 @@ metadata { details(["water", "temperature", "battery", "refresh"]) } } - + def parse(String description) { log.debug "description: $description" @@ -102,29 +103,29 @@ def parse(String description) { else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } - else if (description?.startsWith('zone status')) { - map = parseIasMessage(description) - } - + else if (description?.startsWith('zone status')) { + map = parseIasMessage(description) + } + log.debug "Parse returned $map" def result = map ? createEvent(map) : null - - if (description?.startsWith('enroll request')) { - List cmds = enrollResponse() - log.debug "enroll response: ${cmds}" - result = cmds?.collect { new physicalgraph.device.HubAction(it) } - } - return result + + if (description?.startsWith('enroll request')) { + List cmds = enrollResponse() + log.debug "enroll response: ${cmds}" + result = cmds?.collect { new physicalgraph.device.HubAction(it) } + } + return result } - + private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break + Map resultMap = [:] + def cluster = zigbee.parse(description) + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + resultMap = getBatteryResult(cluster.data.last()) + break case 0x0402: // temp is last 2 data values. reverse to swap endian @@ -139,22 +140,22 @@ private Map parseCatchAllMessage(String description) { } private boolean shouldProcessMessage(cluster) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } - + private Map parseReportAttributeMessage(String description) { Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param -> def nameAndValue = param.split(":") map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] } log.debug "Desc Map: $descMap" - + Map resultMap = [:] if (descMap.cluster == "0402" && descMap.attrId == "0000") { def value = getTemperature(descMap.value) @@ -163,10 +164,10 @@ private Map parseReportAttributeMessage(String description) { else if (descMap.cluster == "0001" && descMap.attrId == "0020") { resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) } - + return resultMap } - + private Map parseCustomMessage(String description) { Map resultMap = [:] if (description?.startsWith('temperature: ')) { @@ -177,42 +178,42 @@ private Map parseCustomMessage(String description) { } private Map parseIasMessage(String description) { - List parsedMsg = description.split(' ') - String msgCode = parsedMsg[2] - - Map resultMap = [:] - switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry - resultMap = getMoistureResult('dry') - break + List parsedMsg = description.split(' ') + String msgCode = parsedMsg[2] - case '0x0021': // Open/Motion/Wet - resultMap = getMoistureResult('wet') - break + Map resultMap = [:] + switch(msgCode) { + case '0x0020': // Closed/No Motion/Dry + resultMap = getMoistureResult('dry') + break - case '0x0022': // Tamper Alarm - break + case '0x0021': // Open/Motion/Wet + resultMap = getMoistureResult('wet') + break - case '0x0023': // Battery Alarm - break + case '0x0022': // Tamper Alarm + break - case '0x0024': // Supervision Report - log.debug 'dry with tamper alarm' - resultMap = getMoistureResult('dry') - break + case '0x0023': // Battery Alarm + break - case '0x0025': // Restore Report - log.debug 'water with tamper alarm' - resultMap = getMoistureResult('wet') - break + case '0x0024': // Supervision Report + log.debug 'dry with tamper alarm' + resultMap = getMoistureResult('dry') + break - case '0x0026': // Trouble/Failure - break + case '0x0025': // Restore Report + log.debug 'water with tamper alarm' + resultMap = getMoistureResult('wet') + break - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0026': // Trouble/Failure + break + + case '0x0028': // Test Mode + break + } + return resultMap } def getTemperature(value) { @@ -242,7 +243,7 @@ private Map getBatteryResult(rawValue) { result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." } else { - if (device.getDataValue("manufacturer") == "SmartThings") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -256,8 +257,7 @@ private Map getBatteryResult(rawValue) { def pct = batteryMap[volts] if (pct != null) { result.value = pct - def value = pct - result.descriptionText = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } else { @@ -265,13 +265,14 @@ private Map getBatteryResult(rawValue) { def maxVolts = 3.0 def pct = (volts - minVolts) / (maxVolts - minVolts) result.value = Math.min(100, (int) pct * 100) - result.descriptionText = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } } return result } + private Map getTemperatureResult(value) { log.debug 'TEMP' if (tempOffset) { @@ -281,9 +282,9 @@ private Map getTemperatureResult(value) { } def descriptionText if ( temperatureScale == 'C' ) - descriptionText = '{{ device.displayName }} was {{ value }}C' + descriptionText = '{{ device.displayName }} was {{ value }}°C' else - descriptionText = '{{ device.displayName }} was {{ value }}F' + descriptionText = '{{ device.displayName }} was {{ value }}°F' return [ name: 'temperature', @@ -294,7 +295,7 @@ private Map getTemperatureResult(value) { } private Map getMoistureResult(value) { - log.debug "water" + log.debug "water" def descriptionText if ( value == "wet" ) descriptionText = '{{ device.displayName }} is wet' @@ -311,7 +312,7 @@ private Map getMoistureResult(value) { def refresh() { log.debug "Refreshing Temperature and Battery" def refreshCmds = [ - "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", + "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200", "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200" ] @@ -321,32 +322,32 @@ def refresh() { def configure() { String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) log.debug "Configuring Reporting, IAS CIE, and Bindings." - def configCmds = [ + def configCmds = [ "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 1", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500", "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs - "send 0x${device.deviceNetworkId} 1 1", "delay 500", + "send 0x${device.deviceNetworkId} 1 1", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500", - "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", - "send 0x${device.deviceNetworkId} 1 1", "delay 500" + "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", + "send 0x${device.deviceNetworkId} 1 1", "delay 500" ] - return configCmds + refresh() // send refresh cmds as part of config + return configCmds + refresh() // send refresh cmds as part of config } def enrollResponse() { log.debug "Sending enroll response" String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) - [ + [ //Resending the CIE in case the enroll request is sent before CIE is written "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", //Enroll Response "raw 0x500 {01 23 00 00 00}", "send 0x${device.deviceNetworkId} 1 1", "delay 200" - ] + ] } private getEndpointId() { @@ -358,19 +359,19 @@ private hex(value) { } private String swapEndianHex(String hex) { - reverseArray(hex.decodeHex()).encodeHex() + reverseArray(hex.decodeHex()).encodeHex() } private byte[] reverseArray(byte[] array) { - int i = 0; - int j = array.length - 1; - byte tmp; - while (j > i) { - tmp = array[j]; - array[j] = array[i]; - array[i] = tmp; - j--; - i++; - } - return array -} \ No newline at end of file + int i = 0; + int j = array.length - 1; + byte tmp; + while (j > i) { + tmp = array[j]; + array[j] = array[i]; + array[i] = tmp; + j--; + i++; + } + return array +} From be220e02b2793c31ca702fd80b303f9f427a824d Mon Sep 17 00:00:00 2001 From: dylanbijnagte Date: Wed, 16 Mar 2016 09:12:06 -0500 Subject: [PATCH 18/23] INTL-290 revert non i18n changes --- .../i18n/messages.properties | 16 +- .../smartsense-multi-sensor.groovy | 265 ++++++++---------- 2 files changed, 126 insertions(+), 155 deletions(-) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties index bcf4fae..2477112 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties @@ -1,16 +1,16 @@ #============================================================================== # 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 +# 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 +# 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 @@ -39,8 +39,6 @@ '''{{ 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 }}입니다. +'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}남아있는 배터리는 {{ value }}%입니다. '''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중 '''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중 -'''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다 -'''{{ device.displayName }} status was opened'''.ko={{ device.displayName }}은(는) 열린 상태입니다 diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index 9bed55b..a33e0e4 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -3,15 +3,15 @@ * 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 + * 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 + * 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 @@ -24,27 +24,28 @@ =============================================================================== */ - metadata { - definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { - capability "Three Axis" +metadata { + definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { + + capability "Three Axis" capability "Battery" - capability "Configuration" - capability "Sensor" + capability "Configuration" + capability "Sensor" capability "Contact Sensor" capability "Acceleration Sensor" capability "Refresh" capability "Temperature Measurement" - + command "enrollResponse" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3320" fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321" - fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor" - fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor" + fingerprint inClusters: "0000,0001,0003,0402,0500,0020,0B05,FC02", outClusters: "0019", manufacturer: "CentraLite", model: "3321-S", deviceJoinName: "Multipurpose Sensor" + fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor" attribute "status", "string" - } + } - simulator { + simulator { status "open": "zone report :: type: 19 value: 0031" status "closed": "zone report :: type: 19 value: 0030" @@ -61,7 +62,7 @@ status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0" status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000" } - preferences { + preferences { section { image(name: 'educationalcontent', multiple: true, images: [ "http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg", @@ -75,9 +76,9 @@ 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?", translatable: true, description: "Tap to set", 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) } - } + } tiles(scale: 2) { multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){ @@ -109,19 +110,16 @@ ] ) } - valueTile("3axis", "device.threeAxis", decoration: "flat", wordWrap: false, width: 2, height: 2) { - state("threeAxis", label:'${currentValue}', unit:"", backgroundColor:"#ffffff") - } valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) { state "battery", label:'${currentValue}% battery', unit:"" } - standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { - state "default", action:"refresh.refresh", icon:"st.secondary.refresh" - } + standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { + state "default", action:"refresh.refresh", icon:"st.secondary.refresh" + } main(["status", "acceleration", "temperature"]) - details(["status", "acceleration", "temperature", "3axis", "battery", "refresh"]) + details(["status", "acceleration", "temperature", "battery", "refresh"]) } } @@ -130,61 +128,61 @@ def parse(String description) { if (description?.startsWith('catchall:')) { map = parseCatchAllMessage(description) } - else if (description?.startsWith('temperature: ')) { + else if (description?.startsWith('temperature: ')) { map = parseCustomMessage(description) } else if (description?.startsWith('zone status')) { map = parseIasMessage(description) } - def result = map ? createEvent(map) : null + def result = map ? createEvent(map) : null if (description?.startsWith('enroll request')) { List cmds = enrollResponse() log.debug "enroll response: ${cmds}" result = cmds?.collect { new physicalgraph.device.HubAction(it) } } - else if (description?.startsWith('read attr -')) { - result = parseReportAttributeMessage(description).each { createEvent(it) } - } + else if (description?.startsWith('read attr -')) { + result = parseReportAttributeMessage(description).each { createEvent(it) } + } return result } - private Map parseCatchAllMessage(String description) { - Map resultMap = [:] - def cluster = zigbee.parse(description) - log.debug cluster - if (shouldProcessMessage(cluster)) { - switch(cluster.clusterId) { - case 0x0001: - resultMap = getBatteryResult(cluster.data.last()) - break +private Map parseCatchAllMessage(String description) { + Map resultMap = [:] + def cluster = zigbee.parse(description) + log.debug cluster + if (shouldProcessMessage(cluster)) { + switch(cluster.clusterId) { + case 0x0001: + resultMap = getBatteryResult(cluster.data.last()) + break - case 0xFC02: - log.debug 'ACCELERATION' - break + case 0xFC02: + log.debug 'ACCELERATION' + break - case 0x0402: - log.debug 'TEMP' - // 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: + log.debug 'TEMP' + // 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) { - // 0x0B is default response indicating message got through - // 0x07 is bind message - boolean ignoredMessage = cluster.profileId != 0x0104 || - cluster.command == 0x0B || - cluster.command == 0x07 || - (cluster.data.size() > 0 && cluster.data.first() == 0x3e) - return !ignoredMessage + // 0x0B is default response indicating message got through + // 0x07 is bind message + boolean ignoredMessage = cluster.profileId != 0x0104 || + cluster.command == 0x0B || + cluster.command == 0x07 || + (cluster.data.size() > 0 && cluster.data.first() == 0x3e) + return !ignoredMessage } private List parseReportAttributeMessage(String description) { @@ -211,10 +209,12 @@ private List parseReportAttributeMessage(String description) { result << parseAxis(threeAxisAttributes) descMap.value = descMap.value[-2..-1] } - result << getAccelerationResult(descMap.value) + result << getAccelerationResult(descMap.value) } else if (descMap.cluster == "FC02" && descMap.attrId == "0012" && descMap.value.size() == 24) { - result << parseAxis(descMap.value) + // The size is checked to ensure the attribute report contains X, Y and Z values + // If all three axis are not included then the attribute report is ignored + result << parseAxis(descMap.value) } else if (descMap.cluster == "0001" && descMap.attrId == "0020") { result << getBatteryResult(Integer.parseInt(descMap.value, 16)) @@ -238,43 +238,43 @@ private Map parseIasMessage(String description) { Map resultMap = [:] switch(msgCode) { - case '0x0020': // Closed/No Motion/Dry + case '0x0020': // Closed/No Motion/Dry if (garageSensor != "Yes"){ resultMap = getContactResult('closed') } - break + break - case '0x0021': // Open/Motion/Wet + case '0x0021': // Open/Motion/Wet if (garageSensor != "Yes"){ resultMap = getContactResult('open') } - break + break - case '0x0022': // Tamper Alarm - break + case '0x0022': // Tamper Alarm + break - case '0x0023': // Battery Alarm - break + case '0x0023': // Battery Alarm + break - case '0x0024': // Supervision Report + case '0x0024': // Supervision Report if (garageSensor != "Yes"){ resultMap = getContactResult('closed') } - break + break - case '0x0025': // Restore Report + case '0x0025': // Restore Report if (garageSensor != "Yes"){ resultMap = getContactResult('open') } - break + break - case '0x0026': // Trouble/Failure - break + case '0x0026': // Trouble/Failure + break - case '0x0028': // Test Mode - break - } - return resultMap + case '0x0028': // Test Mode + break + } + return resultMap } def updated() { @@ -311,7 +311,6 @@ def getTemperature(value) { private Map getBatteryResult(rawValue) { log.debug "Battery rawValue = ${rawValue}" - def linkText = getLinkText(device) def result = [ name: 'battery', @@ -327,7 +326,7 @@ private Map getBatteryResult(rawValue) { result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." } else { - if (device.getDataValue("manufacturer") == "SmartThings") { + if (device.getDataValue("manufacturer") == "SmartThings") { volts = rawValue // For the batteryMap to work the key needs to be an int def batteryMap = [28:100, 27:100, 26:100, 25:90, 24:90, 23:70, 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0] @@ -341,8 +340,7 @@ private Map getBatteryResult(rawValue) { def pct = batteryMap[volts] if (pct != null) { result.value = pct - def value = pct - result.descriptionText = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } else { @@ -350,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 = "{{ device.displayName }} battery was {{ value }}" + result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" } } } @@ -359,56 +357,36 @@ private Map getBatteryResult(rawValue) { } private Map getTemperatureResult(value) { - log.debug 'TEMP' - def name = "temperature" + log.debug "Temperature" if (tempOffset) { def offset = tempOffset as int def v = value as int value = v + offset } - - def descriptionText - if ( temperatureScale == 'C' ) - descriptionText = '{{ device.displayName }} was {{ value }}°C' - else - descriptionText = '{{ device.displayName }} was {{ value }}°F' + def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C': + '{{ device.displayName }} was {{ value }}°F' return [ - name: name, - value: value, - descriptionText: descriptionText, - translatable: true + name: 'temperature', + value: value, + descriptionText: descriptionText, + translatable: true ] } private Map getContactResult(value) { log.debug "Contact: ${device.displayName} value = ${value}" - def name = "contact" - - def descriptionText - if ( value == 'open' ) - descriptionText = '{{ device.displayName }} was opened' - else - descriptionText = '{{ device.displayName }} was closed' - - sendEvent(name: 'status', value: value, descriptionText: descriptionText, displayed: false) - def isStateChange = isStateChange(device, name, value) - return [ - name: name, - value: value, - descriptionText: descriptionText, - isStateChange: isStateChange, - translatable: true - ] - + 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 is $value" + log.debug "Acceleration" def name = "acceleration" def value def descriptionText - + if ( numValue.endsWith("1") ) { value = "active" descriptionText = '{{ device.displayName }} was active' @@ -473,37 +451,38 @@ def refresh() { def configure() { String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) - log.debug "Configuring Device Reporting" - + log.debug "Configuring Reporting" + def configCmds = [ "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs + "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", "delay 200", //checkin time 6 hrs "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200", - "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", + "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200", - "zcl mfg-code ${manufacturerCode}", - "zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", - "zcl mfg-code ${manufacturerCode}", - "zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}", + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", - "zcl mfg-code ${manufacturerCode}", - "zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}", + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", - "zcl mfg-code ${manufacturerCode}", - "zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}", + "zcl mfg-code ${manufacturerCode}", "delay 200", + "zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" - ] + ] + return configCmds + refresh() } @@ -519,17 +498,12 @@ def enrollResponse() { "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", //Enroll Response - "raw 0x500 {01 23 00 00 00}", + "raw 0x500 {01 23 00 00 00}", "delay 200", "send 0x${device.deviceNetworkId} 1 1", "delay 200" ] } private Map parseAxis(String description) { - def hexToSignedInt = { hexVal -> - def unsignedVal = hexToInt(hexVal) - unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal - } - def z = hexToSignedInt(description[0..3]) def y = hexToSignedInt(description[10..13]) def x = hexToSignedInt(description[20..23]) @@ -556,6 +530,11 @@ private Map parseAxis(String description) { getXyzResult(xyzResults, description) } +private hexToSignedInt(hexVal) { + def unsignedVal = hexToInt(hexVal) + unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal +} + def garageEvent(zValue) { def absValue = zValue.abs() def contactValue = null @@ -569,15 +548,9 @@ def garageEvent(zValue) { garageValue = 'garage-open' } if (contactValue != null){ - def linkText = getLinkText(device) - if ( contactValue == 'open' ) { - descriptionText: '{{ device.displayName }} was opened' - sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was opened', displayed:false, translatable: true) - sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was opened', translatable: true) - } else { - sendEvent(name: 'contact', value: contactValue, descriptionText: '{{ device.displayName }} was closed', displayed:false, translatable: true) - sendEvent(name: 'status', value: garageValue, descriptionText: '{{ device.displayName }} status was closed', translatable: true) - } + 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) } } @@ -632,4 +605,4 @@ private byte[] reverseArray(byte[] array) { i++; } return array -} \ No newline at end of file +} From 9cece36d69c5001c2ab1bf7621d75c355f8e993c Mon Sep 17 00:00:00 2001 From: dylanbijnagte Date: Wed, 16 Mar 2016 09:21:24 -0500 Subject: [PATCH 19/23] INTL-353 revert non i18n changes --- .../smartpower-outlet.groovy | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy index ef2b6f4..0dea33e 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy +++ b/devicetypes/smartthings/smartpower-outlet.src/smartpower-outlet.groovy @@ -3,15 +3,15 @@ * 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 + * 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 + * 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 @@ -22,7 +22,6 @@ * 1. 20160117 TW - Update/Edit to support i18n translations =============================================================================== */ - metadata { // Automatically generated. Make future change here. definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") { @@ -104,8 +103,8 @@ def parse(String description) { log.info "$device updates: ${finalResult.value}" } else if (finalResult.type == "power") { - def value = (finalResult.value as Integer)/10 - createEvent(name: "power", value: value, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true ) + def powerValue = (finalResult.value as Integer)/10 + 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 @@ -113,10 +112,8 @@ def parse(String description) { */ } else { - if ( finalResult.value == "on" ) - createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is On', translatable: true) - else - createEvent(name: finalResult.type, value: finalResult.value, descriptionText: '{{ device.displayName }} is Off', translatable: true) + 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 { @@ -177,4 +174,4 @@ def getPowerDescription(descMap) { else { return [:] } -} \ No newline at end of file +} From 0dfbddee381306895f5a55b0b754237ec381da76 Mon Sep 17 00:00:00 2001 From: staticnull Date: Wed, 16 Mar 2016 13:27:55 -0500 Subject: [PATCH 20/23] INTL-414 Add device name translations to device properties files --- .../smartpower-outlet.src/i18n/messages.properties | 13 +++++++------ .../i18n/messages.properties | 1 + .../i18n/messages.properties | 1 + .../i18n/messages.properties | 1 + 4 files changed, 10 insertions(+), 6 deletions(-) diff --git a/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties index 6645906..49ce523 100644 --- a/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartpower-outlet.src/i18n/messages.properties @@ -1,16 +1,16 @@ #============================================================================== # 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 +# 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 +# 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 @@ -23,6 +23,7 @@ # 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 }}꺼졌습니다. diff --git a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties index fc0e2a0..c0a19a0 100644 --- a/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-moisture-sensor.src/i18n/messages.properties @@ -30,6 +30,7 @@ '''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 }}누수 diff --git a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties index 882827f..f06d5ad 100644 --- a/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-motion-sensor.src/i18n/messages.properties @@ -29,6 +29,7 @@ '''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 }}움직임이 중단되었습니다 diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties index 2477112..0f97e6c 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/i18n/messages.properties @@ -32,6 +32,7 @@ '''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 }}닫혔습니다. From 2276748a91fe34fe32285f915752dba96972b913 Mon Sep 17 00:00:00 2001 From: Tom Manley Date: Wed, 16 Mar 2016 17:01:42 -0500 Subject: [PATCH 21/23] multi: Fix occasional error with threeAxis attribute reporting Resolves: https://smartthings.atlassian.net/browse/DVCSMP-1623 --- .../smartsense-multi-sensor.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy index a33e0e4..ab26143 100644 --- a/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy +++ b/devicetypes/smartthings/smartsense-multi-sensor.src/smartsense-multi-sensor.groovy @@ -471,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" ] From 0deb26810da7041b2ff7ec6b41b32f778f9c0495 Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Mar 2016 12:18:14 -0500 Subject: [PATCH 22/23] 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. --- .../vinli-home-connect.src/vinli-home-connect.groovy | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy index 31e2a06..ffa1815 100644 --- a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy +++ b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy @@ -10,9 +10,9 @@ definition( author: "Daniel", description: "Allows Vinli users to connect their car to SmartThings", category: "SmartThings Labs", - iconUrl: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_60.png", - iconX2Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png", - iconX3Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png", + iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", + iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", oauth: true) preferences { @@ -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 } From e2791723831bf1ad3f890cadfe245e79a13031ea Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 17 Mar 2016 12:49:40 -0500 Subject: [PATCH 23/23] Modifying 'Vinli Home Connect' --- .../vinli-home-connect.src/vinli-home-connect.groovy | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy index ffa1815..3bd89fb 100644 --- a/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy +++ b/smartapps/com-vinli-smartthings/vinli-home-connect.src/vinli-home-connect.groovy @@ -10,9 +10,9 @@ definition( author: "Daniel", description: "Allows Vinli users to connect their car to SmartThings", category: "SmartThings Labs", - iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png", - iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", - iconX3Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience@2x.png", + iconUrl: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_60.png", + iconX2Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png", + iconX3Url: "https://d3azp77rte0gip.cloudfront.net/smartapps/baeb2e5d-ebd0-49fe-a4ec-e92417ae20bb/images/vinli_oauth_120.png", oauth: true) preferences {