Compare commits

..

1 Commits

16 changed files with 478 additions and 397 deletions

View File

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

View File

@@ -379,16 +379,12 @@ def getDataByName(String name) {
state[name] ?: device.getDataValue(name) state[name] ?: device.getDataValue(name)
} }
def setThermostatMode(String mode) { def setThermostatMode(String value) {
log.debug "setThermostatMode($mode)" log.debug "setThermostatMode({$value})"
mode = mode.toLowerCase()
switchToMode(mode)
} }
def setThermostatFanMode(String mode) { def setThermostatFanMode(String value) {
log.debug "setThermostatFanMode($mode)" log.debug "setThermostatFanMode({$value})"
mode = mode.toLowerCase()
switchToFanMode(mode)
} }
def generateModeEvent(mode) { def generateModeEvent(mode) {

View File

@@ -1,16 +1,16 @@
#============================================================================== #==============================================================================
# Copyright 2016 SmartThings # Copyright 2016 SmartThings
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may not # 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: # of the License at:
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#============================================================================== #==============================================================================
# Purpose: SmartPower Outlet i18n Translation File # Purpose: SmartPower Outlet i18n Translation File
@@ -23,7 +23,6 @@
# Korean (ko) # Korean (ko)
# Device Preferences # Device Preferences
'''Give your device a name'''.ko=기기 이름 바꾸기 '''Give your device a name'''.ko=기기 이름 바꾸기
'''Outlet'''.ko=플러그
# Events descriptionText # Events descriptionText
'''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다. '''{{ device.displayName }} is On'''.ko={{ device.displayName }}켜졌습니다.
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다. '''{{ device.displayName }} is Off'''.ko={{ device.displayName }}꺼졌습니다.

View File

@@ -3,15 +3,15 @@
* Copyright 2016 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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: * of the License at:
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations * License for the specific language governing permissions and limitations
* under the License. * under the License.
=============================================================================== ===============================================================================
* Purpose: SmartPower Outlet DTH File * Purpose: SmartPower Outlet DTH File
@@ -22,6 +22,7 @@
* 1. 20160117 TW - Update/Edit to support i18n translations * 1. 20160117 TW - Update/Edit to support i18n translations
=============================================================================== ===============================================================================
*/ */
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartPower Outlet", namespace: "smartthings", author: "SmartThings") {
@@ -103,8 +104,8 @@ def parse(String description) {
log.info "$device updates: ${finalResult.value}" log.info "$device updates: ${finalResult.value}"
} }
else if (finalResult.type == "power") { else if (finalResult.type == "power") {
def powerValue = (finalResult.value as Integer)/10 def value = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true ) 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 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 power level is an integer. The exact power level with correct units needs to be handled in the device type
@@ -112,8 +113,10 @@ def parse(String description) {
*/ */
} }
else { else {
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off' if ( finalResult.value == "on" )
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true) 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 { else {
@@ -174,4 +177,4 @@ def getPowerDescription(descMap) {
else { else {
return [:] return [:]
} }
} }

View File

@@ -1,16 +1,16 @@
#============================================================================== #==============================================================================
# Copyright 2016 SmartThings # Copyright 2016 SmartThings
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may not # 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: # of the License at:
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#============================================================================== #==============================================================================
# Purpose: SmartSense Moisture Sensor i18n Translation File # Purpose: SmartSense Moisture Sensor i18n Translation File
@@ -30,12 +30,11 @@
'''Degrees'''.ko=온도 '''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오 '''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기 '''Give your device a name'''.ko=기기 이름 바꾸기
'''Water Leak Sensor'''.ko=누수센서
# Events descriptionText # Events descriptionText
'''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조 '''{{ device.displayName }} is dry'''.ko={{ device.displayName }}가 건조
'''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수 '''{{ device.displayName }} is wet'''.ko={{ device.displayName }}누수
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다 '''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다 '''{{ 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 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 }}입니다.
#============================================================================== #==============================================================================

View File

@@ -3,15 +3,15 @@
* Copyright 2016 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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: * of the License at:
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations * License for the specific language governing permissions and limitations
* under the License. * under the License.
=============================================================================== ===============================================================================
* Purpose: SmartSense Moisture Sensor DTH File * Purpose: SmartSense Moisture Sensor DTH File
@@ -31,18 +31,17 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Water Sensor" 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-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"
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,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" fingerprint inClusters: "0000,0001,0003,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "moisturev4", deviceJoinName: "Water Leak Sensor"
} }
simulator { simulator {
} }
preferences { preferences {
@@ -58,7 +57,7 @@ metadata {
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){ multiAttributeTile(name:"water", type: "generic", width: 6, height: 4){
tileAttribute ("device.water", key: "PRIMARY_CONTROL") { tileAttribute ("device.water", key: "PRIMARY_CONTROL") {
@@ -67,7 +66,7 @@ metadata {
} }
} }
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) { valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°', state "temperature", label:'${currentValue}°',
backgroundColors:[ backgroundColors:[
[value: 31, color: "#153591"], [value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"], [value: 44, color: "#1e9cbb"],
@@ -89,7 +88,7 @@ metadata {
details(["water", "temperature", "battery", "refresh"]) details(["water", "temperature", "battery", "refresh"])
} }
} }
def parse(String description) { def parse(String description) {
log.debug "description: $description" log.debug "description: $description"
@@ -103,29 +102,29 @@ def parse(String description) {
else if (description?.startsWith('temperature: ')) { else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description) map = parseCustomMessage(description)
} }
else if (description?.startsWith('zone status')) { else if (description?.startsWith('zone status')) {
map = parseIasMessage(description) map = parseIasMessage(description)
} }
log.debug "Parse returned $map" log.debug "Parse returned $map"
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = enrollResponse() List cmds = enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) } result = cmds?.collect { new physicalgraph.device.HubAction(it) }
} }
return result return result
} }
private Map parseCatchAllMessage(String description) { private Map parseCatchAllMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
def cluster = zigbee.parse(description) def cluster = zigbee.parse(description)
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
resultMap = getBatteryResult(cluster.data.last()) resultMap = getBatteryResult(cluster.data.last())
break break
case 0x0402: case 0x0402:
// temp is last 2 data values. reverse to swap endian // temp is last 2 data values. reverse to swap endian
@@ -140,22 +139,22 @@ private Map parseCatchAllMessage(String description) {
} }
private boolean shouldProcessMessage(cluster) { private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through // 0x0B is default response indicating message got through
// 0x07 is bind message // 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 || cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
private Map parseReportAttributeMessage(String description) { private Map parseReportAttributeMessage(String description) {
Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param -> Map descMap = (description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":") def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
} }
log.debug "Desc Map: $descMap" log.debug "Desc Map: $descMap"
Map resultMap = [:] Map resultMap = [:]
if (descMap.cluster == "0402" && descMap.attrId == "0000") { if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value) def value = getTemperature(descMap.value)
@@ -164,10 +163,10 @@ private Map parseReportAttributeMessage(String description) {
else if (descMap.cluster == "0001" && descMap.attrId == "0020") { else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
} }
return resultMap return resultMap
} }
private Map parseCustomMessage(String description) { private Map parseCustomMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
if (description?.startsWith('temperature: ')) { if (description?.startsWith('temperature: ')) {
@@ -178,42 +177,42 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
List parsedMsg = description.split(' ') List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2] String msgCode = parsedMsg[2]
Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getMoistureResult('dry')
break
Map resultMap = [:] case '0x0021': // Open/Motion/Wet
switch(msgCode) { resultMap = getMoistureResult('wet')
case '0x0020': // Closed/No Motion/Dry break
resultMap = getMoistureResult('dry')
break
case '0x0021': // Open/Motion/Wet case '0x0022': // Tamper Alarm
resultMap = getMoistureResult('wet') break
break
case '0x0022': // Tamper Alarm case '0x0023': // Battery Alarm
break break
case '0x0023': // Battery Alarm case '0x0024': // Supervision Report
break log.debug 'dry with tamper alarm'
resultMap = getMoistureResult('dry')
break
case '0x0024': // Supervision Report case '0x0025': // Restore Report
log.debug 'dry with tamper alarm' log.debug 'water with tamper alarm'
resultMap = getMoistureResult('dry') resultMap = getMoistureResult('wet')
break break
case '0x0025': // Restore Report case '0x0026': // Trouble/Failure
log.debug 'water with tamper alarm' break
resultMap = getMoistureResult('wet')
break
case '0x0026': // Trouble/Failure case '0x0028': // Test Mode
break break
}
case '0x0028': // Test Mode return resultMap
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -243,7 +242,7 @@ private Map getBatteryResult(rawValue) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
} }
else { 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 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, 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] 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
@@ -257,7 +256,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
if (pct != null) { if (pct != null) {
result.value = pct result.value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" def value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
else { else {
@@ -265,14 +265,13 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100) result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
} }
return result return result
} }
private Map getTemperatureResult(value) { private Map getTemperatureResult(value) {
log.debug 'TEMP' log.debug 'TEMP'
if (tempOffset) { if (tempOffset) {
@@ -282,9 +281,9 @@ private Map getTemperatureResult(value) {
} }
def descriptionText def descriptionText
if ( temperatureScale == 'C' ) if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C' descriptionText = '{{ device.displayName }} was {{ value }}°C'
else else
descriptionText = '{{ device.displayName }} was {{ value }}°F' descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [ return [
name: 'temperature', name: 'temperature',
@@ -295,7 +294,7 @@ private Map getTemperatureResult(value) {
} }
private Map getMoistureResult(value) { private Map getMoistureResult(value) {
log.debug "water" log.debug "water"
def descriptionText def descriptionText
if ( value == "wet" ) if ( value == "wet" )
descriptionText = '{{ device.displayName }} is wet' descriptionText = '{{ device.displayName }} is wet'
@@ -312,7 +311,7 @@ private Map getMoistureResult(value) {
def refresh() { def refresh() {
log.debug "Refreshing Temperature and Battery" log.debug "Refreshing Temperature and Battery"
def refreshCmds = [ 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" "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
] ]
@@ -322,32 +321,32 @@ def refresh() {
def configure() { def configure() {
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings." log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [ def configCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500", "send 0x${device.deviceNetworkId} 1 1", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "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 "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", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}", "zcl global send-me-a-report 0x402 0 0x29 30 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 1", "delay 500" "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() { def enrollResponse() {
log.debug "Sending enroll response" log.debug "Sending enroll response"
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
[ [
//Resending the CIE in case the enroll request is sent before CIE is written //Resending the CIE in case the enroll request is sent before CIE is written
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
//Enroll Response //Enroll Response
"raw 0x500 {01 23 00 00 00}", "raw 0x500 {01 23 00 00 00}",
"send 0x${device.deviceNetworkId} 1 1", "delay 200" "send 0x${device.deviceNetworkId} 1 1", "delay 200"
] ]
} }
private getEndpointId() { private getEndpointId() {
@@ -359,19 +358,19 @@ private hex(value) {
} }
private String swapEndianHex(String hex) { private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex() reverseArray(hex.decodeHex()).encodeHex()
} }
private byte[] reverseArray(byte[] array) { private byte[] reverseArray(byte[] array) {
int i = 0; int i = 0;
int j = array.length - 1; int j = array.length - 1;
byte tmp; byte tmp;
while (j > i) { while (j > i) {
tmp = array[j]; tmp = array[j];
array[j] = array[i]; array[j] = array[i];
array[i] = tmp; array[i] = tmp;
j--; j--;
i++; i++;
} }
return array return array
} }

View File

@@ -1,16 +1,16 @@
#============================================================================== #==============================================================================
# Copyright 2016 SmartThings # Copyright 2016 SmartThings
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may not # 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: # of the License at:
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#============================================================================== #==============================================================================
# Purpose: SmartSense Motion Sensor i18n Translation File # Purpose: SmartSense Motion Sensor i18n Translation File
@@ -29,12 +29,11 @@
'''Degrees'''.ko=온도 '''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오 '''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 바꾸기 '''Give your device a name'''.ko=기기 이름 바꾸기
'''Motion Sensor'''.ko=모션 센서
# Events descriptionText # Events descriptionText
'''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다. '''{{ device.displayName }} detected motion'''.ko={{ device.displayName }} 가 움직임을 감지하였습니다.
'''{{ device.displayName }} motion has stopped'''.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 }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다 '''{{ 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 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 }}입니다.
#============================================================================== #==============================================================================

View File

@@ -3,15 +3,15 @@
* Copyright 2016 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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: * of the License at:
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations * License for the specific language governing permissions and limitations
* under the License. * under the License.
=============================================================================== ===============================================================================
* Purpose: SmartSense Motion Sensor DTH File * Purpose: SmartSense Motion Sensor DTH File
@@ -29,18 +29,17 @@ metadata {
capability "Motion Sensor" capability "Motion Sensor"
capability "Configuration" capability "Configuration"
capability "Battery" capability "Battery"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Refresh" 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: "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: "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: "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: "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"
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,000F,0020,0402,0500", outClusters: "0019", manufacturer: "SmartThings", model: "motionv4", deviceJoinName: "Motion Sensor"
} }
simulator { simulator {
@@ -96,7 +95,7 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description: $description" log.debug "description: $description"
Map map = [:] Map map = [:]
if (description?.startsWith('catchall:')) { if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description) map = parseCatchAllMessage(description)
@@ -107,55 +106,55 @@ def parse(String description) {
else if (description?.startsWith('temperature: ')) { else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description) map = parseCustomMessage(description)
} }
else if (description?.startsWith('zone status')) { else if (description?.startsWith('zone status')) {
map = parseIasMessage(description) map = parseIasMessage(description)
} }
log.debug "Parse returned $map" log.debug "Parse returned $map"
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = enrollResponse() List cmds = enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) } result = cmds?.collect { new physicalgraph.device.HubAction(it) }
} }
return result return result
} }
private Map parseCatchAllMessage(String description) { private Map parseCatchAllMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
def cluster = zigbee.parse(description) def cluster = zigbee.parse(description)
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
resultMap = getBatteryResult(cluster.data.last()) resultMap = getBatteryResult(cluster.data.last())
break break
case 0x0402: case 0x0402:
// temp is last 2 data values. reverse to swap endian // temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp) def value = getTemperature(temp)
resultMap = getTemperatureResult(value) resultMap = getTemperatureResult(value)
break break
case 0x0406: case 0x0406:
log.debug 'motion' log.debug 'motion'
resultMap.name = 'motion' resultMap.name = 'motion'
break break
} }
} }
return resultMap return resultMap
} }
private boolean shouldProcessMessage(cluster) { private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through // 0x0B is default response indicating message got through
// 0x07 is bind message // 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 || cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
private Map parseReportAttributeMessage(String description) { private Map parseReportAttributeMessage(String description) {
@@ -164,7 +163,7 @@ private Map parseReportAttributeMessage(String description) {
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()] map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
} }
log.debug "Desc Map: $descMap" log.debug "Desc Map: $descMap"
Map resultMap = [:] Map resultMap = [:]
if (descMap.cluster == "0402" && descMap.attrId == "0000") { if (descMap.cluster == "0402" && descMap.attrId == "0000") {
def value = getTemperature(descMap.value) def value = getTemperature(descMap.value)
@@ -173,14 +172,14 @@ private Map parseReportAttributeMessage(String description) {
else if (descMap.cluster == "0001" && descMap.attrId == "0020") { else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16)) resultMap = getBatteryResult(Integer.parseInt(descMap.value, 16))
} }
else if (descMap.cluster == "0406" && descMap.attrId == "0000") { else if (descMap.cluster == "0406" && descMap.attrId == "0000") {
def value = descMap.value.endsWith("01") ? "active" : "inactive" def value = descMap.value.endsWith("01") ? "active" : "inactive"
resultMap = getMotionResult(value) resultMap = getMotionResult(value)
} }
return resultMap return resultMap
} }
private Map parseCustomMessage(String description) { private Map parseCustomMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
if (description?.startsWith('temperature: ')) { if (description?.startsWith('temperature: ')) {
@@ -191,44 +190,44 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
List parsedMsg = description.split(' ') List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2] String msgCode = parsedMsg[2]
Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getMotionResult('inactive')
break
Map resultMap = [:] case '0x0021': // Open/Motion/Wet
switch(msgCode) { resultMap = getMotionResult('active')
case '0x0020': // Closed/No Motion/Dry break
resultMap = getMotionResult('inactive')
break
case '0x0021': // Open/Motion/Wet case '0x0022': // Tamper Alarm
resultMap = getMotionResult('active') log.debug 'motion with tamper alarm'
break resultMap = getMotionResult('active')
break
case '0x0022': // Tamper Alarm case '0x0023': // Battery Alarm
log.debug 'motion with tamper alarm' break
resultMap = getMotionResult('active')
break
case '0x0023': // Battery Alarm case '0x0024': // Supervision Report
break log.debug 'no motion with tamper alarm'
resultMap = getMotionResult('inactive')
break
case '0x0024': // Supervision Report case '0x0025': // Restore Report
log.debug 'no motion with tamper alarm' break
resultMap = getMotionResult('inactive')
break
case '0x0025': // Restore Report case '0x0026': // Trouble/Failure
break log.debug 'motion with failure alarm'
resultMap = getMotionResult('active')
break
case '0x0026': // Trouble/Failure case '0x0028': // Test Mode
log.debug 'motion with failure alarm' break
resultMap = getMotionResult('active') }
break return resultMap
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -258,7 +257,7 @@ private Map getBatteryResult(rawValue) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
} }
else { 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 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, 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] 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
@@ -273,7 +272,7 @@ private Map getBatteryResult(rawValue) {
if (pct != null) { if (pct != null) {
result.value = pct result.value = pct
def value = pct def value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
else { else {
@@ -281,7 +280,7 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100) result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
} }
@@ -313,6 +312,7 @@ private Map getTemperatureResult(value) {
private Map getMotionResult(value) { private Map getMotionResult(value) {
log.debug 'motion' log.debug 'motion'
String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped" String descriptionText = value == 'active' ? "{{ device.displayName }} detected motion" : "{{ device.displayName }} motion has stopped"
//String descriptionText = '{{ device.displayName }} is {{ value | translate }}'
return [ return [
name: 'motion', name: 'motion',
value: value, value: value,
@@ -322,7 +322,7 @@ private Map getMotionResult(value) {
} }
def refresh() { def refresh() {
log.debug "refresh called" log.debug "refresh executed"
def refreshCmds = [ 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" "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200"
@@ -372,19 +372,19 @@ private hex(value) {
} }
private String swapEndianHex(String hex) { private String swapEndianHex(String hex) {
reverseArray(hex.decodeHex()).encodeHex() reverseArray(hex.decodeHex()).encodeHex()
} }
private byte[] reverseArray(byte[] array) { private byte[] reverseArray(byte[] array) {
int i = 0; int i = 0;
int j = array.length - 1; int j = array.length - 1;
byte tmp; byte tmp;
while (j > i) { while (j > i) {
tmp = array[j]; tmp = array[j];
array[j] = array[i]; array[j] = array[i];
array[i] = tmp; array[i] = tmp;
j--; j--;
i++; i++;
} }
return array return array
} }

View File

@@ -1,16 +1,16 @@
#============================================================================== #==============================================================================
# Copyright 2016 SmartThings # Copyright 2016 SmartThings
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may not # 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: # of the License at:
# #
# http://www.apache.org/licenses/LICENSE-2.0 # http://www.apache.org/licenses/LICENSE-2.0
# #
# Unless required by applicable law or agreed to in writing, software # Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
#============================================================================== #==============================================================================
# Purpose: SmartSense Multi Sensor i18n Translation File # Purpose: SmartSense Multi Sensor i18n Translation File
@@ -32,7 +32,6 @@
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기 '''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
'''Tap to set'''.ko=눌러서 설정 '''Tap to set'''.ko=눌러서 설정
'''Give your device a name'''.ko=기기 이름 바꾸기 '''Give your device a name'''.ko=기기 이름 바꾸기
'''Multipurpose Sensor'''.ko=멀티 센서
# Events descriptionText # Events descriptionText
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다. '''{{ device.displayName }} was opened'''.ko={{ device.displayName }}열림을 감지하였습니다.
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다. '''{{ device.displayName }} was closed'''.ko={{ device.displayName }}닫혔습니다.
@@ -40,6 +39,8 @@
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다. '''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}비활성화되었습니다.
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다. '''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가){{ value }}°C였습니다.
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다 '''{{ 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 garage sensor'''.ko=기기-차고 센서 업데이트 중
'''Updating device to open/close 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 }}은(는) 열린 상태입니다

View File

@@ -3,15 +3,15 @@
* Copyright 2016 SmartThings * Copyright 2016 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * 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: * of the License at:
* *
* http://www.apache.org/licenses/LICENSE-2.0 * http://www.apache.org/licenses/LICENSE-2.0
* *
* Unless required by applicable law or agreed to in writing, software * Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations * License for the specific language governing permissions and limitations
* under the License. * under the License.
=============================================================================== ===============================================================================
* Purpose: SmartSense Multi Sensor DTH File * Purpose: SmartSense Multi Sensor DTH File
@@ -24,28 +24,27 @@
=============================================================================== ===============================================================================
*/ */
metadata { metadata {
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Three Axis"
capability "Three Axis"
capability "Battery" capability "Battery"
capability "Configuration" capability "Configuration"
capability "Sensor" capability "Sensor"
capability "Contact Sensor" capability "Contact Sensor"
capability "Acceleration Sensor" capability "Acceleration Sensor"
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
command "enrollResponse" 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: "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"
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,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,000F,0020,0402,0500,FC02", outClusters: "0019", manufacturer: "SmartThings", model: "multiv4", deviceJoinName: "Multipurpose Sensor"
attribute "status", "string" attribute "status", "string"
} }
simulator { simulator {
status "open": "zone report :: type: 19 value: 0031" status "open": "zone report :: type: 19 value: 0031"
status "closed": "zone report :: type: 19 value: 0030" status "closed": "zone report :: type: 19 value: 0030"
@@ -62,7 +61,7 @@ metadata {
status "x,y,z: 0,1000,0": "x: 0, y: 1000, z: 0" 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" status "x,y,z: 0,0,1000": "x: 0, y: 0, z: 1000"
} }
preferences { preferences {
section { section {
image(name: 'educationalcontent', multiple: true, images: [ image(name: 'educationalcontent', multiple: true, images: [
"http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg", "http://cdn.device-gse.smartthings.com/Multi/Multi1.jpg",
@@ -76,9 +75,9 @@ metadata {
input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
section { section {
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) 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) { tiles(scale: 2) {
multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){ multiAttributeTile(name:"status", type: "generic", width: 6, height: 4){
@@ -110,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) { valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:"" state "battery", label:'${currentValue}% battery', unit:""
} }
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main(["status", "acceleration", "temperature"]) main(["status", "acceleration", "temperature"])
details(["status", "acceleration", "temperature", "battery", "refresh"]) details(["status", "acceleration", "temperature", "3axis", "battery", "refresh"])
} }
} }
@@ -128,61 +130,61 @@ def parse(String description) {
if (description?.startsWith('catchall:')) { if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description) map = parseCatchAllMessage(description)
} }
else if (description?.startsWith('temperature: ')) { else if (description?.startsWith('temperature: ')) {
map = parseCustomMessage(description) map = parseCustomMessage(description)
} }
else if (description?.startsWith('zone status')) { else if (description?.startsWith('zone status')) {
map = parseIasMessage(description) map = parseIasMessage(description)
} }
def result = map ? createEvent(map) : null def result = map ? createEvent(map) : null
if (description?.startsWith('enroll request')) { if (description?.startsWith('enroll request')) {
List cmds = enrollResponse() List cmds = enrollResponse()
log.debug "enroll response: ${cmds}" log.debug "enroll response: ${cmds}"
result = cmds?.collect { new physicalgraph.device.HubAction(it) } result = cmds?.collect { new physicalgraph.device.HubAction(it) }
} }
else if (description?.startsWith('read attr -')) { else if (description?.startsWith('read attr -')) {
result = parseReportAttributeMessage(description).each { createEvent(it) } result = parseReportAttributeMessage(description).each { createEvent(it) }
} }
return result return result
} }
private Map parseCatchAllMessage(String description) { private Map parseCatchAllMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
def cluster = zigbee.parse(description) def cluster = zigbee.parse(description)
log.debug cluster log.debug cluster
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
resultMap = getBatteryResult(cluster.data.last()) resultMap = getBatteryResult(cluster.data.last())
break break
case 0xFC02: case 0xFC02:
log.debug 'ACCELERATION' log.debug 'ACCELERATION'
break break
case 0x0402: case 0x0402:
log.debug 'TEMP' log.debug 'TEMP'
// temp is last 2 data values. reverse to swap endian // temp is last 2 data values. reverse to swap endian
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join() String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
def value = getTemperature(temp) def value = getTemperature(temp)
resultMap = getTemperatureResult(value) resultMap = getTemperatureResult(value)
break break
} }
} }
return resultMap return resultMap
} }
private boolean shouldProcessMessage(cluster) { private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through // 0x0B is default response indicating message got through
// 0x07 is bind message // 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 || cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
private List parseReportAttributeMessage(String description) { private List parseReportAttributeMessage(String description) {
@@ -209,12 +211,10 @@ private List parseReportAttributeMessage(String description) {
result << parseAxis(threeAxisAttributes) result << parseAxis(threeAxisAttributes)
descMap.value = descMap.value[-2..-1] 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) { 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 result << parseAxis(descMap.value)
// 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") { else if (descMap.cluster == "0001" && descMap.attrId == "0020") {
result << getBatteryResult(Integer.parseInt(descMap.value, 16)) result << getBatteryResult(Integer.parseInt(descMap.value, 16))
@@ -238,43 +238,43 @@ private Map parseIasMessage(String description) {
Map resultMap = [:] Map resultMap = [:]
switch(msgCode) { switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry case '0x0020': // Closed/No Motion/Dry
if (garageSensor != "Yes"){ if (garageSensor != "Yes"){
resultMap = getContactResult('closed') resultMap = getContactResult('closed')
} }
break break
case '0x0021': // Open/Motion/Wet case '0x0021': // Open/Motion/Wet
if (garageSensor != "Yes"){ if (garageSensor != "Yes"){
resultMap = getContactResult('open') resultMap = getContactResult('open')
} }
break break
case '0x0022': // Tamper Alarm case '0x0022': // Tamper Alarm
break break
case '0x0023': // Battery Alarm case '0x0023': // Battery Alarm
break break
case '0x0024': // Supervision Report case '0x0024': // Supervision Report
if (garageSensor != "Yes"){ if (garageSensor != "Yes"){
resultMap = getContactResult('closed') resultMap = getContactResult('closed')
} }
break break
case '0x0025': // Restore Report case '0x0025': // Restore Report
if (garageSensor != "Yes"){ if (garageSensor != "Yes"){
resultMap = getContactResult('open') resultMap = getContactResult('open')
} }
break break
case '0x0026': // Trouble/Failure case '0x0026': // Trouble/Failure
break break
case '0x0028': // Test Mode case '0x0028': // Test Mode
break break
} }
return resultMap return resultMap
} }
def updated() { def updated() {
@@ -311,6 +311,7 @@ def getTemperature(value) {
private Map getBatteryResult(rawValue) { private Map getBatteryResult(rawValue) {
log.debug "Battery rawValue = ${rawValue}" log.debug "Battery rawValue = ${rawValue}"
def linkText = getLinkText(device)
def result = [ def result = [
name: 'battery', name: 'battery',
@@ -326,7 +327,7 @@ private Map getBatteryResult(rawValue) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts."
} }
else { 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 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, 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] 22:70, 21:50, 20:50, 19:30, 18:30, 17:15, 16:1, 15:0]
@@ -340,7 +341,8 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
if (pct != null) { if (pct != null) {
result.value = pct result.value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" def value = pct
result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
else { else {
@@ -348,7 +350,7 @@ private Map getBatteryResult(rawValue) {
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100) result.value = Math.min(100, (int) pct * 100)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%" result.descriptionText = "{{ device.displayName }} battery was {{ value }}"
} }
} }
} }
@@ -357,36 +359,56 @@ private Map getBatteryResult(rawValue) {
} }
private Map getTemperatureResult(value) { private Map getTemperatureResult(value) {
log.debug "Temperature" log.debug 'TEMP'
def name = "temperature"
if (tempOffset) { if (tempOffset) {
def offset = tempOffset as int def offset = tempOffset as int
def v = value as int def v = value as int
value = v + offset value = v + offset
} }
def descriptionText = temperatureScale == 'C' ? '{{ device.displayName }} was {{ value }}°C':
'{{ device.displayName }} was {{ value }}°F' def descriptionText
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [ return [
name: 'temperature', name: name,
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText,
translatable: true translatable: true
] ]
} }
private Map getContactResult(value) { private Map getContactResult(value) {
log.debug "Contact: ${device.displayName} value = ${value}" log.debug "Contact: ${device.displayName} value = ${value}"
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed' def name = "contact"
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true)
sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true) 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
]
} }
private getAccelerationResult(numValue) { private getAccelerationResult(numValue) {
log.debug "Acceleration" log.debug "Acceleration is $value"
def name = "acceleration" def name = "acceleration"
def value def value
def descriptionText def descriptionText
if ( numValue.endsWith("1") ) { if ( numValue.endsWith("1") ) {
value = "active" value = "active"
descriptionText = '{{ device.displayName }} was active' descriptionText = '{{ device.displayName }} was active'
@@ -451,38 +473,37 @@ def refresh() {
def configure() { def configure() {
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting" log.debug "Configuring Device Reporting"
def configCmds = [ def configCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 200", "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", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0x402 {${device.zigbeeId}} {}", "delay 200", "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", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200", "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 0xFC02 {${device.zigbeeId}} {}", "delay 200",
"zcl mfg-code ${manufacturerCode}", "delay 200", "zcl mfg-code ${manufacturerCode}",
"zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}", "delay 200", "zcl global send-me-a-report 0xFC02 0x0010 0x18 10 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200", "zcl mfg-code ${manufacturerCode}",
"zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {0100}", "delay 200", "zcl global send-me-a-report 0xFC02 0x0012 0x29 1 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200", "zcl mfg-code ${manufacturerCode}",
"zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {0100}", "delay 200", "zcl global send-me-a-report 0xFC02 0x0013 0x29 1 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
"zcl mfg-code ${manufacturerCode}", "delay 200", "zcl mfg-code ${manufacturerCode}",
"zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {0100}", "delay 200", "zcl global send-me-a-report 0xFC02 0x0014 0x29 1 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
] ]
return configCmds + refresh() return configCmds + refresh()
} }
@@ -498,12 +519,17 @@ def enrollResponse() {
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200", "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500", "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
//Enroll Response //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" "send 0x${device.deviceNetworkId} 1 1", "delay 200"
] ]
} }
private Map parseAxis(String description) { private Map parseAxis(String description) {
def hexToSignedInt = { hexVal ->
def unsignedVal = hexToInt(hexVal)
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
}
def z = hexToSignedInt(description[0..3]) def z = hexToSignedInt(description[0..3])
def y = hexToSignedInt(description[10..13]) def y = hexToSignedInt(description[10..13])
def x = hexToSignedInt(description[20..23]) def x = hexToSignedInt(description[20..23])
@@ -530,11 +556,6 @@ private Map parseAxis(String description) {
getXyzResult(xyzResults, description) getXyzResult(xyzResults, description)
} }
private hexToSignedInt(hexVal) {
def unsignedVal = hexToInt(hexVal)
unsignedVal > 32767 ? unsignedVal - 65536 : unsignedVal
}
def garageEvent(zValue) { def garageEvent(zValue) {
def absValue = zValue.abs() def absValue = zValue.abs()
def contactValue = null def contactValue = null
@@ -548,9 +569,15 @@ def garageEvent(zValue) {
garageValue = 'garage-open' garageValue = 'garage-open'
} }
if (contactValue != null){ if (contactValue != null){
def descriptionText = contactValue == 'open' ? '{{ device.displayName }} was opened' :'{{ device.displayName }} was closed' def linkText = getLinkText(device)
sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false, translatable: true) if ( contactValue == 'open' ) {
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true) 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)
}
} }
} }
@@ -605,4 +632,4 @@ private byte[] reverseArray(byte[] array) {
i++; i++;
} }
return array return array
} }

View File

@@ -203,7 +203,7 @@ private List parseContactMessage(String description) {
parts.each { part -> parts.each { part ->
part = part.trim() part = part.trim()
if (part.startsWith('contactState:')) { if (part.startsWith('contactState:')) {
results.addAll(getContactResult(part, description)) results << getContactResult(part, description)
} }
else if (part.startsWith('accelerationState:')) { else if (part.startsWith('accelerationState:')) {
results << getAccelerationResult(part, description) results << getAccelerationResult(part, description)
@@ -316,7 +316,7 @@ private List getContactResult(part, description) {
results results
} }
private Map getAccelerationResult(part, description) { private getAccelerationResult(part, description) {
def name = "acceleration" def name = "acceleration"
def value = part.endsWith("1") ? "active" : "inactive" def value = part.endsWith("1") ? "active" : "inactive"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -335,7 +335,7 @@ private Map getAccelerationResult(part, description) {
] ]
} }
private Map getTempResult(part, description) { private getTempResult(part, description) {
def name = "temperature" def name = "temperature"
def temperatureScale = getTemperatureScale() def temperatureScale = getTemperatureScale()
def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale) def value = zigbee.parseSmartThingsTemperatureValue(part, "temp: ", temperatureScale)
@@ -360,7 +360,7 @@ private Map getTempResult(part, description) {
] ]
} }
private Map getXyzResult(results, description) { private getXyzResult(results, description) {
def name = "threeAxis" def name = "threeAxis"
def value = "${results.x},${results.y},${results.z}" def value = "${results.x},${results.y},${results.z}"
def linkText = getLinkText(device) def linkText = getLinkText(device)
@@ -379,7 +379,7 @@ private Map getXyzResult(results, description) {
] ]
} }
private Map getBatteryResult(part, description) { private getBatteryResult(part, description) {
def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null def batteryDivisor = description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"} ? description.split(",").find {it.split(":")[0].trim() == "batteryDivisor"}.split(":")[1].trim() : null
def name = "battery" def name = "battery"
def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor) def value = zigbee.parseSmartThingsBatteryValue(part, batteryDivisor)
@@ -400,7 +400,7 @@ private Map getBatteryResult(part, description) {
] ]
} }
private Map getRssiResult(part, description, lastHop=false) { private getRssiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopRssi" : "rssi" def name = lastHop ? "lastHopRssi" : "rssi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def value = (Integer.parseInt(valueString) - 128).toString() def value = (Integer.parseInt(valueString) - 128).toString()
@@ -431,7 +431,7 @@ private Map getRssiResult(part, description, lastHop=false) {
* Note: To make the signal strength indicator more accurate, we could combine * Note: To make the signal strength indicator more accurate, we could combine
* LQI with RSSI. * LQI with RSSI.
*/ */
private Map getLqiResult(part, description, lastHop=false) { private getLqiResult(part, description, lastHop=false) {
def name = lastHop ? "lastHopLqi" : "lqi" def name = lastHop ? "lastHopLqi" : "lqi"
def valueString = part.split(":")[1].trim() def valueString = part.split(":")[1].trim()
def percentageOf = 255 def percentageOf = 255

View File

@@ -67,11 +67,11 @@ mappings {
def listAllDevices() { def listAllDevices() {
def resp = [] def resp = []
switches.each { 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 { 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 return resp
} }

View File

@@ -469,6 +469,7 @@ def pollChild(){
if (pollChildren()){ if (pollChildren()){
devices.each { child -> devices.each { child ->
log.info "***found $child"
if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){ if (!child.device.deviceNetworkId.startsWith("ecobee_sensor")){
if(atomicState.thermostats[child.device.deviceNetworkId] != null) { if(atomicState.thermostats[child.device.deviceNetworkId] != null) {
def tData = atomicState.thermostats[child.device.deviceNetworkId] def tData = atomicState.thermostats[child.device.deviceNetworkId]

View File

@@ -0,0 +1,62 @@
/**
* Copyright 2015 SmartThings
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
* CoolDeep
*
* Author: CoolDeep
*/
definition(
name: "Smart Timer",
namespace: "smartthings",
author: "CoolDeep",
description: "If a light/device is already on, leave it on. Else, if a light/device switches on on a trigger, switch off in a given time",
category: "My Apps",
iconUrl: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience.png",
iconX2Url: "https://s3.amazonaws.com/smartapp-icons/Convenience/Cat-Convenience%402x.png"
)
preferences {
section("When a sesonsor triggers(turns on)...") {
input "mySwitch", "capability.switch", title: "Switches", multiple: true, required: true
input "mySensor", "capability.contactSensor", title: "Sensors", multiple: true, required: true
}
section("Turn it off after how many minutes?") {
input "minutesLater", "decimal", title: "When?"
}
}
def installed() {
log.debug "Installed with settings: ${settings}"
subscribe(mySensor, "contact.opened", myHandler)
}
def updated() {
log.debug "Updated with settings: ${settings}"
unsubscribe()
subscribe(mySensor, "contact.open", myHandler)
}
def myHandler(evt) {
def lightVal = mySwitch.currentSwitch
log.debug ">> TheSwitch current value turned ${lightVal}"
if (lightVal.contains("off")) {
def delay = minutesLater * 60
log.debug ">>> Turning off in ${minutesLater} minutes (${delay}seconds)"
runIn(delay, turnOffSwitch)
}
}
def turnOffSwitch() {
mySwitch.off()
}

View File

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

View File

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