Compare commits

..

1 Commits

Author SHA1 Message Date
윤정은
d15a108efd MSA-944: test 2016-03-09 22:37:47 -06:00
184 changed files with 3131 additions and 12283 deletions

View File

@@ -1,25 +1,22 @@
import java.nio.charset.StandardCharsets import java.nio.charset.StandardCharsets
import java.nio.file.Paths import java.nio.file.Paths
import com.smartthings.deployment.slack.FileUpload
import com.smartthings.deployment.slack.Message
apply plugin: 'groovy' apply plugin: 'groovy'
apply plugin: 'smartthings-executable-deployment' apply plugin: 'smartthings-executable-deployment'
apply plugin: 'smartthings-slack' apply plugin: 'smartthings-hipchat'
buildscript { buildscript {
dependencies { dependencies {
classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.8" classpath "com.smartthings.deployment:executable-deployment-scripts:1.0.3"
} }
repositories { repositories {
mavenLocal()
jcenter() jcenter()
maven { maven {
credentials { credentials {
username smartThingsArtifactoryUserName username smartThingsArtifactoryUserName
password smartThingsArtifactoryPassword password smartThingsArtifactoryPassword
} }
url "https://artifactory.smartthings.com/libs-release-local" url "http://artifactory.smartthings.com/libs-release-local"
} }
} }
} }
@@ -27,76 +24,12 @@ buildscript {
repositories { repositories {
mavenLocal() mavenLocal()
jcenter() jcenter()
maven {
credentials {
username smartThingsArtifactoryUserName
password smartThingsArtifactoryPassword
}
url "https://artifactory.smartthings.com/libs-release-local"
}
}
sourceSets {
devicetypes {
groovy {
srcDirs = ['devicetypes']
}
}
smartapps {
groovy {
srcDirs = ['smartapps']
}
}
} }
dependencies { dependencies {
devicetypesCompile 'org.codehaus.groovy:groovy-all:2.4.7'
devicetypesCompile 'smartthings:appengine-z-wave:0.1.2'
devicetypesCompile 'smartthings:appengine-zigbee:0.1.11'
smartappsCompile 'org.codehaus.groovy:groovy-all:2.4.7'
smartappsCompile 'smartthings:appengine-common:0.1.8'
smartappsCompile 'org.codehaus.groovy.modules.http-builder:http-builder:0.7.1'
smartappsCompile 'org.grails:grails-web:2.3.11'
smartappsCompile 'org.json:json:20140107'
} }
slackSendMessage { hipchatShareFile {
String branch = project.hasProperty('branch') ? project.property('branch') : 'unknown'
String token = project.hasProperty('slackToken') ? project.property('slackToken') : null
String webhookUrl = project.hasProperty('slackWebhookUrl') ? project.property('slackWebhookUrl') : null
String channel = project.hasProperty('slackChannel') ? project.property('slackChannel') : null
String drinks = 'https://dl.dropboxusercontent.com/s/m1z5mpd3c83lwev/minion_beer.jpeg?dl=0'
String wolverine = 'https://dl.dropboxusercontent.com/s/4lbjqzvm2v033u9/minion_wolverine.jpg?dl=0'
String beach = 'https://dl.dropboxusercontent.com/s/rqrfgxk53gfng69/minion_beach.png?dl=0'
String iconUrl
String color
String messageText
String username
switch (branch) {
case 'master':
username = 'Hickory'
iconUrl = wolverine
color = '#35D0F2'
messageText = 'Began deployment of _SmartThingsPublic[master]_ branch to the _Dev_ environments.'
break
case 'staging':
username = 'Dickory'
iconUrl = beach
color = '#FFDE20'
messageText = 'Began deployment of _SmartThingsPublic[staging]_ branch to the _Staging_ environments.'
break
case 'production':
username = 'Dock'
iconUrl = drinks
color = '#FF1D23'
messageText = 'Began deployment of _SmartThingsPublic[production]_ branch to the _Prod_ environments.'
break
default:
username = 'Hickory'
iconUrl = wolverine
color = '#35D0F2'
messageText = "Began deployment of an _SmartThingsPublic[${branch}]_ branch. Have no idea what's going on."
}
List<String> archives = [] List<String> archives = []
File rootDir = new File("${project.buildDir}/archives") File rootDir = new File("${project.buildDir}/archives")
if (rootDir.exists()) { if (rootDir.exists()) {
@@ -109,25 +42,16 @@ slackSendMessage {
} }
} }
} }
Date date = new Date()
String fileDate = date.format('yyyy-MM-dd_HH-mm-ss', TimeZone.getTimeZone('GMT'))
// Required Task Arguments. // Set task properties
file = new FileUpload( data = archives.join('\n').getBytes(StandardCharsets.UTF_8)
data: archives.join('\n').getBytes(StandardCharsets.UTF_8), fileName = 'deployment-notes.txt'
filename: "deployment-notes-${fileDate}.txt", contentType = 'text/html'
title: 'Deployment Notes', }
channels: channel,
token: token, hipchatSendNotification {
color: color String branch = project.hasProperty('branch') ? project.property('branch') : 'unknown'
) message = "Began executable deploy of SmartThingsPublic(${branch})."
message = new Message( color = branch == 'master' ? 'yellow' : 'red'
webhookUrl: webhookUrl, notify = true
username: username,
asUser: true,
iconUrl: iconUrl,
channel: channel,
fallback: 'Deployment Notification',
text: messageText
)
} }

View File

@@ -5,9 +5,7 @@ machine:
dependencies: dependencies:
override: override:
- ./gradlew dependencies -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" - echo "Nothing to do."
post:
- ./gradlew compileSmartappsGroovy compileDevicetypesGroovy -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD"
test: test:
override: override:
@@ -17,11 +15,13 @@ deployment:
develop: develop:
branch: master branch: master
commands: commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_DEV" - ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_DEV
- ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL" --stacktrace - ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD
stage: stage:
branch: staging branch: staging
commands: commands:
- ./gradlew deployArchives -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Ps3Buckets="$S3_BUCKETS_STAGE" - ./gradlew deployArchives -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Ps3BucketName=$S3_BUCKET_NAME_PREPROD_STAGING
- ./gradlew slackSendMessage -PsmartThingsArtifactoryUserName="$ARTIFACTORY_USERNAME" -PsmartThingsArtifactoryPassword="$ARTIFACTORY_PASSWORD" -Pbranch="$CIRCLE_BRANCH" -PslackToken="$SLACK_TOKEN" -PslackWebhookUrl="$SLACK_WEBHOOK_URL" -PslackChannel="$SLACK_CHANNEL" --stacktrace - ./gradlew hipchatSendNotification -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD -Pbranch=$CIRCLE_BRANCH
- ./gradlew hipchatShareFile -PsmartThingsArtifactoryUserName=$ARTIFACTORY_USERNAME -PsmartThingsArtifactoryPassword=$ARTIFACTORY_PASSWORD

View File

@@ -15,7 +15,6 @@
*/ */
metadata { metadata {
definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") { definition (name: "Netatmo Additional Module", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Temperature Measurement" capability "Temperature Measurement"

View File

@@ -15,7 +15,6 @@
*/ */
metadata { metadata {
definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") { definition (name: "Netatmo Basestation", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Temperature Measurement" capability "Temperature Measurement"

View File

@@ -15,7 +15,6 @@
*/ */
metadata { metadata {
definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") { definition (name: "Netatmo Outdoor Module", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Temperature Measurement" capability "Temperature Measurement"
} }

View File

@@ -15,8 +15,6 @@
*/ */
metadata { metadata {
definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") { definition (name: "Netatmo Rain", namespace: "dianoga", author: "Brian Steere") {
capability "Sensor"
attribute "rain", "number" attribute "rain", "number"
attribute "rainSumHour", "number" attribute "rainSumHour", "number"
attribute "rainSumDay", "number" attribute "rainSumDay", "number"

View File

@@ -1,272 +0,0 @@
/**
* Fibaro Door/Window Sensor ZW5
*
* Copyright 2016 Fibar Group S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Fibaro Door/Window Sensor ZW5 with Temperature", namespace: "fibargroup", author: "Fibar Group S.A.") {
capability "Battery"
capability "Contact Sensor"
capability "Sensor"
capability "Configuration"
capability "Tamper Alert"
capability "Temperature Measurement"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x31, 0x86", outClusters: ""
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x31, 0x86, 0x84", outClusters: ""//actual NIF
}
simulator {
}
tiles(scale: 2) {
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
main "FGK"
details(["FGK","battery", "temperature"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
def result = []
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (cmd) {
log.debug "Parsed '${cmd}'"
zwaveEvent(cmd)
}
}
}
//security
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x98: 1])
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
{
def versions = [0x31: 5, 0x72: 2, 0x80: 1, 0x86: 1]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
//it is assumed that default notification events are used
//(parameter 20 was not changed before device's re-inclusion)
def map = [:]
if (cmd.notificationType == 6) {
switch (cmd.event) {
case 22:
map.name = "contact"
map.value = "open"
map.descriptionText = "${device.displayName}: is open"
break
case 23:
map.name = "contact"
map.value = "closed"
map.descriptionText = "${device.displayName}: is closed"
break
}
} else if (cmd.notificationType == 7) {
switch (cmd.event) {
case 0:
map.name = "tamper"
map.value = "inactive"
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
break
case 3:
map.name = "tamper"
map.value = "active"
map.descriptionText = "${device.displayName}: tamper alarm activated"
break
}
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
map.unit = "%"
map.displayed = true
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
def cmds = []
cmds << encap(zwave.batteryV1.batteryGet())
cmds << "delay 500"
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
cmds << "delay 1200"
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
[event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "manufacturerId: ${cmd.manufacturerId}"
log.debug "manufacturerName: ${cmd.manufacturerName}"
log.debug "productId: ${cmd.productId}"
log.debug "productTypeId: ${cmd.productTypeId}"
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
log.debug "deviceIdData: ${cmd.deviceIdData}"
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
log.debug "deviceIdType: ${cmd.deviceIdType}"
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
String serialNumber = "h'"
cmd.deviceIdData.each{ data ->
serialNumber += "${String.format("%02X", data)}"
}
updateDataValue("serialNumber", serialNumber)
log.debug "${device.displayName} - serial number: ${serialNumber}"
}
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
log.debug "applicationVersion: ${cmd.applicationVersion}"
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
def map = [:]
if (cmd.sensorType == 1) {
// temperature
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
map.name = "temperature"
map.displayed = true
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def configure() {
log.debug "Executing 'configure'"
def cmds = []
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGK's default wake up interval
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
cmds += zwave.versionV1.versionGet()
cmds += zwave.batteryV1.batteryGet()
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
encapSequence(cmds, 500)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crc16(physicalgraph.zwave.Command cmd) {
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
"5601${cmd.format()}0000"
}
private encapSequence(commands, delay=200) {
delayBetween(commands.collect{ encap(it) }, delay)
}
private encap(physicalgraph.zwave.Command cmd) {
def secureClasses = [0x20, 0x2B, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
//todo: check if secure inclusion was successful
//if not do not send security-encapsulated command
if (secureClasses.find{ it == cmd.commandClassId }) {
secure(cmd)
} else {
crc16(cmd)
}
}

View File

@@ -1,239 +0,0 @@
/**
* Fibaro Door/Window Sensor ZW5
*
* Copyright 2016 Fibar Group S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Fibaro Door/Window Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
capability "Battery"
capability "Contact Sensor"
capability "Sensor"
capability "Configuration"
capability "Tamper Alert"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x85, 0x59, 0x22, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x2B, 0x9C, 0x30, 0x86, 0x84", outClusters: ""
}
simulator {
}
tiles(scale: 2) {
multiAttributeTile(name:"FGK", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
tileAttribute("device.contact", key:"PRIMARY_CONTROL") {
attributeState("open", icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
attributeState("closed", icon:"st.contact.contact.closed", backgroundColor:"#79b821")
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}
valueTile("battery", "device.battery", inactiveLabel: false, , width: 2, height: 2, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
main "FGK"
details(["FGK","battery"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
def result = []
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (cmd) {
log.debug "Parsed '${cmd}'"
zwaveEvent(cmd)
}
}
}
//security
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x98: 1])
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
{
def versions = [0x72: 2, 0x80: 1, 0x86: 1]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
//it is assumed that default notification events are used
//(parameter 20 was not changed before device's re-inclusion)
def map = [:]
if (cmd.notificationType == 6) {
switch (cmd.event) {
case 22:
map.name = "contact"
map.value = "open"
map.descriptionText = "${device.displayName}: is open"
break
case 23:
map.name = "contact"
map.value = "closed"
map.descriptionText = "${device.displayName}: is closed"
break
}
} else if (cmd.notificationType == 7) {
switch (cmd.event) {
case 0:
map.name = "tamper"
map.value = "inactive"
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
break
case 3:
map.name = "tamper"
map.value = "active"
map.descriptionText = "${device.displayName}: tamper alarm activated"
break
}
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
map.unit = "%"
map.displayed = true
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd) {
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
def cmds = []
cmds << encap(zwave.batteryV1.batteryGet())
cmds << "delay 1200"
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
[event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "manufacturerId: ${cmd.manufacturerId}"
log.debug "manufacturerName: ${cmd.manufacturerName}"
log.debug "productId: ${cmd.productId}"
log.debug "productTypeId: ${cmd.productTypeId}"
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
log.debug "deviceIdData: ${cmd.deviceIdData}"
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
log.debug "deviceIdType: ${cmd.deviceIdType}"
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
String serialNumber = "h'"
cmd.deviceIdData.each{ data ->
serialNumber += "${String.format("%02X", data)}"
}
updateDataValue("serialNumber", serialNumber)
log.debug "${device.displayName} - serial number: ${serialNumber}"
}
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
log.debug "applicationVersion: ${cmd.applicationVersion}"
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def configure() {
log.debug "Executing 'configure'"
def cmds = []
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGK's default wake up interval
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
cmds += zwave.versionV1.versionGet()
cmds += zwave.batteryV1.batteryGet()
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
encapSequence(cmds, 500)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crc16(physicalgraph.zwave.Command cmd) {
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
"5601${cmd.format()}0000"
}
private encapSequence(commands, delay=200) {
delayBetween(commands.collect{ encap(it) }, delay)
}
private encap(physicalgraph.zwave.Command cmd) {
def secureClasses = [0x20, 0x2B, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
//todo: check if secure inclusion was successful
//if not do not send security-encapsulated command
if (secureClasses.find{ it == cmd.commandClassId }) {
secure(cmd)
} else {
crc16(cmd)
}
}

View File

@@ -1,269 +0,0 @@
/**
* Fibaro Flood Sensor ZW5
*
* Copyright 2016 Fibar Group S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Fibaro Flood Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
capability "Battery"
capability "Configuration"
capability "Sensor"
capability "Tamper Alert"
capability "Temperature Measurement"
capability "Water Sensor"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x22, 0x85, 0x59, 0x20, 0x80, 0x70, 0x56, 0x5A, 0x7A, 0x72, 0x8E, 0x71, 0x73, 0x98, 0x9C, 0x31, 0x86", outClusters: ""
}
simulator {
}
tiles(scale: 2) {
multiAttributeTile(name:"FGFS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
tileAttribute("device.water", key:"PRIMARY_CONTROL") {
attributeState("dry", icon:"st.alarm.water.dry", backgroundColor:"#79b821")
attributeState("wet", icon:"st.alarm.water.wet", backgroundColor:"#ffa81e")
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("battery", "device.battery", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "battery", label:'${currentValue}% battery', unit:""
}
main "FGFS"
details(["FGFS","battery", "temperature"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
def result = []
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "FGFS failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72:2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (cmd) {
log.debug "Parsed '${cmd}'"
zwaveEvent(cmd)
}
}
}
//security
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
{
def versions = [0x31: 5, 0x72: 2, 0x80: 1]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
def cmds = []
cmds << encap(zwave.batteryV1.batteryGet())
cmds << "delay 500"
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
cmds << "delay 1200"
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
[event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "manufacturerId: ${cmd.manufacturerId}"
log.debug "manufacturerName: ${cmd.manufacturerName}"
log.debug "productId: ${cmd.productId}"
log.debug "productTypeId: ${cmd.productTypeId}"
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
log.debug "deviceIdData: ${cmd.deviceIdData}"
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
log.debug "deviceIdType: ${cmd.deviceIdType}"
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
String serialNumber = "h'"
cmd.deviceIdData.each{ data ->
serialNumber += "${String.format("%02X", data)}"
}
updateDataValue("serialNumber", serialNumber)
log.debug "${device.displayName} - serial number: ${serialNumber}"
}
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
log.debug "applicationVersion: ${cmd.applicationVersion}"
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
map.unit = "%"
map.displayed = true
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
def map = [:]
if (cmd.notificationType == 5) {
switch (cmd.event) {
case 2:
map.name = "water"
map.value = "wet"
map.descriptionText = "${device.displayName} is ${map.value}"
break
case 0:
map.name = "water"
map.value = "dry"
map.descriptionText = "${device.displayName} is ${map.value}"
break
}
} else if (cmd.notificationType == 7) {
switch (cmd.event) {
case 0:
map.name = "tamper"
map.value = "inactive"
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
break
case 3:
map.name = "tamper"
map.value = "active"
map.descriptionText = "${device.displayName}: tamper alarm activated"
break
}
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
def map = [:]
if (cmd.sensorType == 1) {
// temperature
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
map.unit = getTemperatureScale()
map.name = "temperature"
map.displayed = true
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def configure() {
log.debug "Executing 'configure'"
def cmds = []
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds:21600, nodeid: zwaveHubNodeId)//FGFS' default wake up interval
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
cmds += zwave.versionV1.versionGet()
cmds += zwave.batteryV1.batteryGet()
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId: [zwaveHubNodeId])
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
encapSequence(cmds, 500)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crc16(physicalgraph.zwave.Command cmd) {
//zwave.crc16EncapV1.crc16Encap().encapsulate(cmd).format()
"5601${cmd.format()}0000"
}
private encapSequence(commands, delay=200) {
delayBetween(commands.collect{ encap(it) }, delay)
}
private encap(physicalgraph.zwave.Command cmd) {
def secureClasses = [0x20, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
//todo: check if secure inclusion was successful
//if not do not send security-encapsulated command
if (secureClasses.find{ it == cmd.commandClassId }) {
secure(cmd)
} else {
crc16(cmd)
}
}

View File

@@ -1,282 +0,0 @@
/**
* Fibaro Motion Sensor ZW5
*
* Copyright 2016 Fibar Group S.A.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "Fibaro Motion Sensor ZW5", namespace: "fibargroup", author: "Fibar Group S.A.") {
capability "Battery"
capability "Configuration"
capability "Illuminance Measurement"
capability "Motion Sensor"
capability "Sensor"
capability "Tamper Alert"
capability "Temperature Measurement"
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x20, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x30, 0x9C, 0x98, 0x7A", outClusters: ""
}
simulator {
}
tiles(scale: 2) {
multiAttributeTile(name:"FGMS", type:"lighting", width:6, height:4) {//with generic type secondary control text is not displayed in Android app
tileAttribute("device.motion", key:"PRIMARY_CONTROL") {
attributeState("inactive", label:"no motion", icon:"st.motion.motion.inactive", backgroundColor:"#79b821")
attributeState("active", label:"motion", icon:"st.motion.motion.active", backgroundColor:"#ffa81e")
}
tileAttribute("device.tamper", key:"SECONDARY_CONTROL") {
attributeState("active", label:'tamper active', backgroundColor:"#53a7c0")
attributeState("inactive", label:'tamper inactive', backgroundColor:"#ffffff")
}
}
valueTile("temperature", "device.temperature", inactiveLabel: false, width: 2, height: 2) {
state "temperature", label:'${currentValue}°',
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
}
valueTile("illuminance", "device.illuminance", inactiveLabel: false, width: 2, height: 2) {
state "luminosity", label:'${currentValue} ${unit}', unit:"lux"
}
valueTile("battery", "device.battery", inactiveLabel: false, width: 2, height: 2, decoration: "flat") {
state "battery", label:'${currentValue}% battery', unit:""
}
main "FGMS"
details(["FGMS","battery","temperature","illuminance"])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
def result = []
if (description.startsWith("Err 106")) {
if (state.sec) {
result = createEvent(descriptionText:description, displayed:false)
} else {
result = createEvent(
descriptionText: "FGK failed to complete the network security key exchange. If you are unable to receive data from it, you must remove it from your network and add it again.",
eventType: "ALERT",
name: "secureInclusion",
value: "failed",
displayed: true,
)
}
} else if (description == "updated") {
return null
} else {
def cmd = zwave.parse(description, [0x31: 5, 0x56: 1, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (cmd) {
log.debug "Parsed '${cmd}'"
zwaveEvent(cmd)
}
}
}
//security
def zwaveEvent(physicalgraph.zwave.commands.securityv1.SecurityMessageEncapsulation cmd) {
def encapsulatedCommand = cmd.encapsulatedCommand([0x71: 3, 0x84: 2, 0x85: 2, 0x86: 1, 0x98: 1])
if (encapsulatedCommand) {
return zwaveEvent(encapsulatedCommand)
} else {
log.warn "Unable to extract encapsulated cmd from $cmd"
createEvent(descriptionText: cmd.toString())
}
}
//crc16
def zwaveEvent(physicalgraph.zwave.commands.crc16encapv1.Crc16Encap cmd)
{
def versions = [0x31: 5, 0x71: 3, 0x72: 2, 0x80: 1, 0x84: 2, 0x85: 2, 0x86: 1]
def version = versions[cmd.commandClass as Integer]
def ccObj = version ? zwave.commandClass(cmd.commandClass, version) : zwave.commandClass(cmd.commandClass)
def encapsulatedCommand = ccObj?.command(cmd.command)?.parse(cmd.data)
if (!encapsulatedCommand) {
log.debug "Could not extract command from $cmd"
} else {
zwaveEvent(encapsulatedCommand)
}
}
def zwaveEvent(physicalgraph.zwave.commands.sensormultilevelv5.SensorMultilevelReport cmd) {
def map = [ displayed: true ]
switch (cmd.sensorType) {
case 1:
def cmdScale = cmd.scale == 1 ? "F" : "C"
map.name = "temperature"
map.unit = getTemperatureScale()
map.value = convertTemperatureIfNeeded(cmd.scaledSensorValue, cmdScale, cmd.precision)
break
case 3:
map.name = "illuminance"
map.value = cmd.scaledSensorValue.toInteger().toString()
map.unit = "lux"
break
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.notificationv3.NotificationReport cmd) {
def map = [:]
if (cmd.notificationType == 7) {
switch (cmd.event) {
case 0:
if (cmd.eventParameter[0] == 3) {
map.name = "tamper"
map.value = "inactive"
map.descriptionText = "${device.displayName}: tamper alarm has been deactivated"
}
if (cmd.eventParameter[0] == 8) {
map.name = "motion"
map.value = "inactive"
map.descriptionText = "${device.displayName}: motion has stopped"
}
break
case 3:
map.name = "tamper"
map.value = "active"
map.descriptionText = "${device.displayName}: tamper alarm activated"
break
case 8:
map.name = "motion"
map.value = "active"
map.descriptionText = "${device.displayName}: motion detected"
break
}
}
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.batteryv1.BatteryReport cmd) {
def map = [:]
map.name = "battery"
map.value = cmd.batteryLevel == 255 ? 1 : cmd.batteryLevel.toString()
map.unit = "%"
map.displayed = true
createEvent(map)
}
def zwaveEvent(physicalgraph.zwave.commands.wakeupv2.WakeUpNotification cmd)
{
def event = createEvent(descriptionText: "${device.displayName} woke up", displayed: false)
def cmds = []
cmds << encap(zwave.batteryV1.batteryGet())
cmds << "delay 500"
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0))
cmds << "delay 500"
cmds << encap(zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3, scale: 1))
cmds << "delay 1200"
cmds << encap(zwave.wakeUpV1.wakeUpNoMoreInformation())
[event, response(cmds)]
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerSpecificReport cmd) {
log.debug "manufacturerId: ${cmd.manufacturerId}"
log.debug "manufacturerName: ${cmd.manufacturerName}"
log.debug "productId: ${cmd.productId}"
log.debug "productTypeId: ${cmd.productTypeId}"
}
def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.DeviceSpecificReport cmd) {
log.debug "deviceIdData: ${cmd.deviceIdData}"
log.debug "deviceIdDataFormat: ${cmd.deviceIdDataFormat}"
log.debug "deviceIdDataLengthIndicator: ${cmd.deviceIdDataLengthIndicator}"
log.debug "deviceIdType: ${cmd.deviceIdType}"
if (cmd.deviceIdType == 1 && cmd.deviceIdDataFormat == 1) {//serial number in binary format
String serialNumber = "h'"
cmd.deviceIdData.each{ data ->
serialNumber += "${String.format("%02X", data)}"
}
updateDataValue("serialNumber", serialNumber)
log.debug "${device.displayName} - serial number: ${serialNumber}"
}
}
def zwaveEvent(physicalgraph.zwave.commands.versionv1.VersionReport cmd) {
updateDataValue("version", "${cmd.applicationVersion}.${cmd.applicationSubVersion}")
log.debug "applicationVersion: ${cmd.applicationVersion}"
log.debug "applicationSubVersion: ${cmd.applicationSubVersion}"
log.debug "zWaveLibraryType: ${cmd.zWaveLibraryType}"
log.debug "zWaveProtocolVersion: ${cmd.zWaveProtocolVersion}"
log.debug "zWaveProtocolSubVersion: ${cmd.zWaveProtocolSubVersion}"
}
def zwaveEvent(physicalgraph.zwave.commands.deviceresetlocallyv1.DeviceResetLocallyNotification cmd) {
log.info "${device.displayName}: received command: $cmd - device has reset itself"
}
def configure() {
log.debug "Executing 'configure'"
def cmds = []
cmds += zwave.wakeUpV2.wakeUpIntervalSet(seconds: 7200, nodeid: zwaveHubNodeId)//FGMS' default wake up interval
cmds += zwave.manufacturerSpecificV2.manufacturerSpecificGet()
cmds += zwave.manufacturerSpecificV2.deviceSpecificGet()
cmds += zwave.versionV1.versionGet()
cmds += zwave.associationV2.associationSet(groupingIdentifier:1, nodeId:[zwaveHubNodeId])
cmds += zwave.batteryV1.batteryGet()
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 1, scale: 0)
cmds += zwave.sensorMultilevelV5.sensorMultilevelGet(sensorType: 3, scale: 1)
cmds += zwave.wakeUpV2.wakeUpNoMoreInformation()
encapSequence(cmds, 500)
}
private secure(physicalgraph.zwave.Command cmd) {
zwave.securityV1.securityMessageEncapsulation().encapsulate(cmd).format()
}
private crc16(physicalgraph.zwave.Command cmd) {
//zwave.crc16encapV1.crc16Encap().encapsulate(cmd).format()
"5601${cmd.format()}0000"
}
private encapSequence(commands, delay=200) {
delayBetween(commands.collect{ encap(it) }, delay)
}
private encap(physicalgraph.zwave.Command cmd) {
def secureClasses = [0x20, 0x30, 0x5A, 0x70, 0x71, 0x84, 0x85, 0x8E, 0x9C]
//todo: check if secure inclusion was successful
//if not do not send security-encapsulated command
if (secureClasses.find{ it == cmd.commandClassId }) {
secure(cmd)
} else {
crc16(cmd)
}
}

View File

@@ -1,795 +0,0 @@
/**
* CoopBoss H3Vx
* 02/29/16 Fixed app crash with Android by changing the syntax of default state in tile definition.
* Fixed null value errors during join process. Added 3 new commands to refresh data.
*
* 01/18/16 Masked invalid temperature reporting when TempProbe1 is below 0C
* Added setBaseCurrentNE, readBaseCurrentNE, commands as well as baseCurrentNE attribute.
*
* Copyright 2016 John Rucker
*
* 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.
* Icon location = http://scripts.3dgo.net/smartthings/icons/
*/
metadata {
definition (name: "CoopBoss H3Vx", namespace: "JohnRucker", author: "John.Rucker@Solar-Current.com") {
capability "Refresh"
capability "Polling"
capability "Sensor"
capability "Actuator"
capability "Configuration"
capability "Temperature Measurement"
capability "Door Control"
capability "Switch"
command "closeDoor"
command "closeDoorHiI"
command "openDoor"
command "autoCloseOn"
command "autoCloseOff"
command "autoOpenOn"
command "autoOpenOff"
command "setCloseLevelTo"
command "setOpenLevelTo"
command "setSensitivityLevel"
command "Aux1On"
command "Aux1Off"
command "Aux2On"
command "Aux2Off"
command "updateTemp1"
command "updateTemp2"
command "updateSun"
command "setNewBaseCurrent"
command "setNewPhotoCalibration"
command "readNewPhotoCalibration"
command "readBaseCurrentNE"
command "setBaseCurrentNE"
command "updateSensitivity"
command "updateCloseLightLevel"
command "updateOpenLightLevel"
attribute "doorState","string"
attribute "currentLightLevel","number"
attribute "closeLightLevel","number"
attribute "openLightLevel","number"
attribute "autoCloseEnable","string"
attribute "autoOpenEnable","string"
attribute "TempProb1","number"
attribute "TempProb2","number"
attribute "dayOrNight","string"
attribute "doorSensitivity","number"
attribute "doorCurrent","number"
attribute "doorVoltage","number"
attribute "Aux1","string"
attribute "Aux2","string"
attribute "coopStatus","string"
attribute "baseDoorCurrent","number"
attribute "photoCalibration","number"
attribute "baseCurrentNE","string"
fingerprint profileId: "0104", inClusters: "0000,0101,0402", manufacturer: "Solar-Current", model: "Coop Boss"
}
// simulator metadata
simulator {
}
preferences {
input 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 "tempOffsetCoop", "number", title: "Coop Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
input "tempOffsetOutside", "number", title: "Outside Temperature Offset", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
}
// UI tile definitions
tiles(scale: 2){
multiAttributeTile(name:"doorCtrl", type:"generic", width:6, height:4) {tileAttribute("device.doorState", key: "PRIMARY_CONTROL")
{
attributeState "unknown", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", nextState:"Sent"
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#0000ff" , nextState:"Sent"
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffa81e"
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#79b821", nextState:"Sent"
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ffa81e"
attributeState "jammed", label: '${name}', action:"closeDoorHiI", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff0000", nextState:"Sent"
attributeState "forced close", label: 'forced\rclose', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff8000", nextState:"Sent"
attributeState "fault", label: 'FAULT', action:"openDoor", icon: "st.Outdoor.outdoor20", backgroundColor: "#ff0000", nextState:"Sent"
attributeState "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
tileAttribute ("device.coopStatus", key: "SECONDARY_CONTROL") {
attributeState "device.coopStatus", label:'${currentValue}'
}
}
multiAttributeTile(name:"dtlsDoorCtrl", type:"generic", width:6, height:4) {tileAttribute("device.doorState", key: "PRIMARY_CONTROL")
{
attributeState "unknown", label: '${name}', action:"openDoor", icon: "st.secondary.tools", nextState:"Sent"
attributeState "open", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-open", backgroundColor: "#0000ff", nextState:"Sent"
attributeState "opening", label: '${name}', action:"closeDoor", icon: "st.doors.garage.garage-opening", backgroundColor: "#ffa81e"
attributeState "closed", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closed", backgroundColor: "#79b821", nextState:"Sent"
attributeState "closing", label: '${name}', action:"openDoor", icon: "st.doors.garage.garage-closing", backgroundColor: "#ffa81e"
attributeState "jammed", label: '${name}', action:"closeDoorHiI", icon: "st.doors.garage.garage-open", backgroundColor: "#ff0000", nextState:"Sent"
attributeState "forced close", label: "forced", action:"openDoor", icon: "st.doors.garage.garage-closed", backgroundColor: "#ff8000", nextState:"Sent"
attributeState "fault", label: 'FAULT', action:"openDoor", icon: "st.secondary.tools", backgroundColor: "#ff0000", nextState:"Sent"
attributeState "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
tileAttribute ("device.doorState", key: "SECONDARY_CONTROL") {
attributeState "unknown", label: 'Door is in unknown state. Push to open.'
attributeState "open", label: 'Coop door is open. Push to close.'
attributeState "opening", label: 'Caution, door is opening!'
attributeState "closed", label: 'Coop door is closed. Push to open.'
attributeState "closing", label: 'Caution, door is closing!'
attributeState "jammed", label: 'Door open! Push for high-force close'
attributeState "forced close", label: "Door is closed. Push to open."
attributeState "fault", label: 'Door fault check electrical connection.'
attributeState "Sent", label: 'Command sent to CoopBoss...'
}
}
standardTile("autoClose", "device.autoCloseEnable", width: 2, height: 2){
state "on", label: 'Auto', action:"autoCloseOff", icon: "st.doors.garage.garage-closing", backgroundColor: "#79b821", nextState:"Sent"
state "off", label: 'Auto', action:"autoCloseOn", icon: "st.doors.garage.garage-closing", nextState:"Sent"
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
standardTile("autoOpen", "device.autoOpenEnable", width: 2, height: 2){
state "on", label: 'Auto', action:"autoOpenOff", icon: "st.doors.garage.garage-opening", backgroundColor: "#79b821", nextState:"Sent"
state "off", label: 'Auto', action:"autoOpenOn", icon: "st.doors.garage.garage-opening", nextState:"Sent"
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
valueTile("TempProb1", "device.TempProb1", width: 2, height: 2, decoration: "flat"){
state "default", label:'Coop\r${currentValue}°', unit:"F", action:"updateTemp1"}
valueTile("TempProb2", "device.TempProb2", width: 2, height: 2, decoration: "flat"){
state "default", label:'Outside\r${currentValue}°', unit:"F", action:"updateTemp2"}
valueTile("currentLevel", "device.currentLightLevel", width: 2, height: 2, decoration: "flat") {
state "default", label:'Sun\r${currentValue}', action:"updateSun"}
valueTile("dayOrNight", "device.dayOrNight", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "default", label:'${currentValue}.'
}
controlTile("SetClSlider", "device.closeLightLevel", "slider", height: 2, width: 4, inactiveLabel: false, range:"(1..100)") {
state "closeLightLevel", action:"setCloseLevelTo", backgroundColor:"#d04e00"
}
valueTile("SetClValue", "device.closeLightLevel", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "default", label:'Close\nSunlight\n${currentValue}', action:'updateCloseLightLevel'
}
controlTile("SetOpSlider", "device.openLightLevel", "slider", height: 2, width: 4, inactiveLabel: false, range:"(1..100)") {
state "openLightLevel", action:"setOpenLevelTo", backgroundColor:"#d04e00"
}
valueTile("SetOpValue", "device.openLightLevel", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "default", label:'Open\nSunlight\n${currentValue}', action:'updateOpenLightLevel'
}
controlTile("SetSensitivitySlider", "device.doorSensitivity", "slider", height: 2, width: 4, inactiveLabel: false, range:"(1..100)") {
state "openLightLevel", action:"setSensitivityLevel", backgroundColor:"#d04e00"
}
valueTile("SetSensitivityValue", "device.doorSensitivity", decoration: "flat", inactiveLabel: false, width: 2, height: 2) {
state "default", label:'Door\nSensitivity\n${currentValue}', action:'updateSensitivity'
}
standardTile("refresh", "device.refresh", width: 2, height: 2, decoration: "flat", inactiveLabel: false) {
state "default", label:'All', action:"refresh.refresh", icon:"st.secondary.refresh-icon"
}
standardTile("aux1", "device.Aux1", width: 2, height: 2, canChangeIcon: true) {
state "off", label:'Aux 1', action:"Aux1On", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"Sent"
state "on", label:'Aux 1', action:"Aux1Off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"Sent"
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
standardTile("aux2", "device.Aux2", width: 2, height: 2, canChangeIcon: true) {
state "off", label:'Aux 2', action:"Aux2On", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"Sent"
state "on", label:'Aux 2', action:"Aux2Off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"Sent"
state "Sent", label: 'wait', icon: "st.motion.motion.active", backgroundColor: "#ffa81e"
}
main "doorCtrl"
details (["dtlsDoorCtrl", "TempProb1", "TempProb2", "currentLevel", "autoClose", "autoOpen", "dayOrNight",
"SetClSlider", "SetClValue", "SetOpSlider", "SetOpValue", "SetSensitivitySlider", "SetSensitivityValue",
"aux1", "aux2", "refresh"])
}
}
// Parse incoming device messages to generate events def parse(String description) {
def parse(String description) {
log.debug "description: $description"
Map map = [:]
if (description?.startsWith('catchall:')) {
map = parseCatchAllMessage(description)
}
else if (description?.startsWith('read attr -')) {
map = parseReportAttributeMessage(description)
}
else if (description?.startsWith('temperature: ') || description?.startsWith('humidity: ')) {
map = parseCustomMessage(description)
}
log.debug map
//return map ? createEvent(map) : null
sendEvent(map)
callUpdateStatusTxt()
}
private Map parseCatchAllMessage(String description) {
Map resultMap = [:]
def cluster = zigbee.parse(description)
log.debug cluster
if (cluster.clusterId == 0x0402) {
switch(cluster.sourceEndpoint) {
case 0x39: // Endpoint 0x39 is the temperature of probe 1
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
resultMap.name = "TempProb1"
def celsius = Integer.valueOf(temp,16).shortValue()
if (celsius == -32768){ // This number is used to indicate an error in the temperature reading
resultMap.value = "---"
}else{
celsius = celsius / 100 // Temperature value is sent X 100.
resultMap.value = celsiusToFahrenheit(celsius)
if (tempOffsetOutside) {
def offset = tempOffsetOutside as int
resultMap.value = resultMap.value + offset
}
}
sendEvent(name: "temperature", value: resultMap.value, displayed: false) // set the temperatureMeasurment capability to temperature
break
case 0x40: // Endpoint 0x40 is the temperature of probe 2
String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
resultMap.name = "TempProb2"
def celsius = Integer.valueOf(temp,16).shortValue()
//resultMap.descriptionText = "Prob2 celsius value = ${celsius}"
if (celsius == -32768){ // This number is used to indicate an error in the temperature reading
resultMap.value = "---"
}else{
celsius = celsius / 100 // Temperature value is sent X 100.
resultMap.value = celsiusToFahrenheit(celsius)
if (tempOffsetCoop) {
def offset = tempOffsetCoop as int
resultMap.value = resultMap.value + offset
}
}
break
}
}
if (cluster.clusterId == 0x0101 && cluster.command == 0x0b) { // This is a default response to a command sent to cluster 0x0101 door control
//log.debug "Default Response Data = $cluster.data"
switch(cluster.data) {
case "[10, 0]": // 0x0a turn auto close on command verified
resultMap.name = "autoCloseEnable"
resultMap.value = "on"
break
case "[11, 0]": // 0x0b turn auto close off command verified
resultMap.name = "autoCloseEnable"
resultMap.value = "off"
break
case "[12, 0]": // 0x0C turn auto open on command verified
resultMap.name = "autoOpenEnable"
resultMap.value = "on"
break
case "[13, 0]": // 0x0d turn auto open off command verified
resultMap.name = "autoOpenEnable"
resultMap.value = "off"
break
case "[20, 0]": // 0x14 Aux1 On command verified
log.info "verified Aux1 On"
sendEvent(name: "switch", value: "on", displayed: false)
resultMap.name = "Aux1"
resultMap.value = "on"
break
case "[21, 0]": // 0x15 Aux1 Off command verified
log.info "verified Aux1 Off"
sendEvent(name: "switch", value: "off", displayed: false)
resultMap.name = "Aux1"
resultMap.value = "off"
break
case "[22, 0]": // 0x16 Aux2 On command verified
log.info "verified Aux2 On"
resultMap.name = "Aux2"
resultMap.value = "on"
break
case "[23, 0]": // 0x17 Aux2 Off command verified
log.info "verified Aux2 Off"
resultMap.name = "Aux2"
resultMap.value = "off"
break
}
}
return resultMap
}
private Map parseReportAttributeMessage(String description) {
Map resultMap = [:]
def descMap = parseDescriptionAsMap(description)
//log.debug "read attr descMap --> $descMap"
if (descMap.cluster == "0101" && descMap.attrId == "0003") {
resultMap.name = "doorState"
if (descMap.value == "00"){
resultMap.value = "unknown"
sendEvent(name: "door", value: "unknown", displayed: false)
}else if(descMap.value == "01"){
resultMap.value = "closed"
sendEvent(name: "door", value: "closed", displayed: false)
}else if(descMap.value == "02"){
resultMap.value = "open"
sendEvent(name: "door", value: "open", displayed: false)
}else if(descMap.value == "03"){
resultMap.value = "jammed"
}else if(descMap.value == "04"){
resultMap.value = "forced close"
}else if(descMap.value == "05"){
resultMap.value = "forced close"
}else if(descMap.value == "06"){
resultMap.value = "closing"
sendEvent(name: "door", value: "closing", displayed: false)
}else if(descMap.value == "07"){
resultMap.value = "opening"
sendEvent(name: "door", value: "opening", displayed: false)
}else if(descMap.value == "08"){
resultMap.value = "fault"
}else {
resultMap.value = "unknown"
}
resultMap.descriptionText = "Door State Changed to ${resultMap.value}"
} else if (descMap.cluster == "0101" && descMap.attrId == "0400") {
resultMap.name = "currentLightLevel"
resultMap.value = (Integer.parseInt(descMap.value, 16))
resultMap.displayed = false
} else if (descMap.cluster == "0101" && descMap.attrId == "0401") {
resultMap.name = "closeLightLevel"
resultMap.value = (Integer.parseInt(descMap.value, 16))
} else if (descMap.cluster == "0101" && descMap.attrId == "0402") {
resultMap.name = "openLightLevel"
resultMap.value = (Integer.parseInt(descMap.value, 16))
} else if (descMap.cluster == "0101" && descMap.attrId == "0403") {
resultMap.name = "autoCloseEnable"
if (descMap.value == "01"){resultMap.value = "on"}
else{resultMap.value = "off"}
} else if (descMap.cluster == "0101" && descMap.attrId == "0404") {
resultMap.name = "autoOpenEnable"
if (descMap.value == "01"){resultMap.value = "on"}
else{resultMap.value = "off"}
} else if (descMap.cluster == "0101" && descMap.attrId == "0405") {
resultMap.name = "doorCurrent"
resultMap.value = (Integer.parseInt(descMap.value, 16))
resultMap.value = resultMap.value * 0.001
} else if (descMap.cluster == "0101" && descMap.attrId == "0408") {
resultMap.name = "doorSensitivity"
resultMap.value = (100 - Integer.parseInt(descMap.value, 16))
} else if (descMap.cluster == "0101" && descMap.attrId == "0409") {
resultMap.name = "baseDoorCurrent"
resultMap.value = (Integer.parseInt(descMap.value, 16))
resultMap.value = resultMap.value * 0.001
} else if (descMap.cluster == "0101" && descMap.attrId == "040a") {
resultMap.name = "doorVoltage"
resultMap.value = (Integer.parseInt(descMap.value, 16))
resultMap.value = resultMap.value * 0.001
} else if (descMap.cluster == "0101" && descMap.attrId == "040b") {
resultMap.name = "Aux1"
if(descMap.value == "01"){
resultMap.value = "on"
sendEvent(name: "switch", value: "on", displayed: false)
}else{
resultMap.value = "off"
sendEvent(name: "switch", value: "off", displayed: false)
}
} else if (descMap.cluster == "0101" && descMap.attrId == "040c") {
resultMap.name = "Aux2"
if(descMap.value == "01"){
resultMap.value = "on"
}else{
resultMap.value = "off"
}
} else if (descMap.cluster == "0101" && descMap.attrId == "040d") {
resultMap.name = "photoCalibration"
resultMap.value = (Integer.parseInt(descMap.value, 16))
} else if (descMap.cluster == "0101" && descMap.attrId == "040e") {
resultMap.name = "baseCurrentNE"
resultMap.value = (Integer.parseInt(descMap.value, 16))
}
return resultMap
}
private Map parseCustomMessage(String description) {
//log.info "ParseCustomMessage called with ${description}"
Map resultMap = [:]
if (description?.startsWith('temperature: ')) {
resultMap.name = "temperature"
def rawT = (description - "temperature: ").trim()
resultMap.descriptionText = "Temperature celsius value = ${rawT}"
def rawTint = Float.parseFloat(rawT)
if (rawTint > 65){
resultMap.name = null
resultMap.value = null
resultMap.descriptionText = "Temperature celsius value = ${rawT} is invalid not updating"
log.warn "Invalid temperature value detected! rawT = ${rawT}, description = ${description}"
}else if (rawT == -32768){ // This number is used to indicate an error in the temperature reading
resultMap.value = "ERR"
}else{
resultMap.value = celsiusToFahrenheit(rawT.toFloat()) as Float
sendEvent(name: "TempProb1", value: resultMap.value, displayed: false) // Workaround for lack of access to endpoint information for Temperature report
}
}
resultMap.displayed = false
log.info "Temperature reported = ${resultMap.value}"
return resultMap
}
def parseDescriptionAsMap(description) {
(description - "read attr - ").split(",").inject([:]) { map, param ->
def nameAndValue = param.split(":")
map += [(nameAndValue[0].trim()):nameAndValue[1].trim()]
}
}
// Added for Temeperature parse
def getFahrenheit(value) {
def celsius = Integer.parseInt(value, 16)
return celsiusToFahrenheit(celsius) as Integer
}
// Private methods
def callUpdateStatusTxt(){
def cTemp = device.currentState("TempProb1")?.value
def cLight = 0
def testNull = device.currentState("currentLightLevel")?.value
if (testNull != null){
cLight = device.currentState("currentLightLevel")?.value as int
}
updateStatusTxt(cTemp, cLight)
}
def updateStatusTxt(currentTemp, currentLight){
//log.info "called updateStatusTxt with ${currentTemp}, ${currentLight}"
def cTmp = currentTemp
def cLL = 10
def oLL = 10
def testNull = device.currentState("closeLightLevel")?.value
if (testNull != null){
cLL = device.currentState("closeLightLevel")?.value as int
}
testNull = device.currentState("openLightLevel")?.value
if (testNull != null){
oLL = device.currentState("openLightLevel")?.value as int
}
def aOpnEn = device.currentState("autoOpenEnable")?.value
def aClsEn = device.currentState("autoCloseEnable")?.value
if (currentLight < cLL){
if (aOpnEn == "on"){
sendEvent(name: "dayOrNight", value: "Sun must be > ${oLL} to auto open", displayed: false)
sendEvent(name: "coopStatus", value: "Sunlight ${currentLight} open at ${oLL}. Coop ${cTmp}°", displayed: false)
}else{
sendEvent(name: "dayOrNight", value: "Auto Open is turned off.", displayed: false)
sendEvent(name: "coopStatus", value: "Sunlight ${currentLight} auto open off. Coop ${cTmp}°", displayed: false)
}
}else {
if (aClsEn == "on"){
sendEvent(name: "dayOrNight", value: "Sun must be < ${cLL} to auto close", displayed: false)
sendEvent(name: "coopStatus", value: "Sunlight ${currentLight} close at ${cLL}. Coop ${cTmp}°", displayed: false)
}else{
sendEvent(name: "dayOrNight", value: "Auto Close is turned off.", displayed: false)
sendEvent(name: "coopStatus", value: "Sunlight ${currentLight} auto close off. Coop ${cTmp}°", displayed: false)
}
}
}
// Commands to device
def on() {
log.debug "on calling Aux1On"
Aux1On()
}
def off() {
log.debug "off calling Aux1Off"
Aux1Off()
}
def close() {
log.debug "close calling closeDoor"
closeDoor()
}
def open() {
log.debug "open calling openDoor"
openDoor()
}
def Aux1On(){
log.debug "Sending Aux1 = on command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x14 {}"
}
def Aux1Off(){
log.debug "Sending Aux1 = off command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x15 {}"
}
def Aux2On(){
log.debug "Sending Aux2 = on command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x16 {}"
}
def Aux2Off(){
log.debug "Sending Aux2 = off command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x17 {}"
}
def openDoor() {
log.debug "Sending Open command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x1 {}"
}
def closeDoor() {
log.debug "Sending Close command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x0 {}"
}
def closeDoorHiI() {
log.debug "Sending High Current Close command"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x4 {}"
}
def autoOpenOn() {
log.debug "Setting Auto Open On"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x0C {}"
}
def autoOpenOff() {
log.debug "Setting Auto Open Off"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x0D {}"
}
def autoCloseOn() {
log.debug "Setting Auto Close On"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x0A {}"
}
def autoCloseOff() {
log.debug "Setting Auto Close Off"
"st cmd 0x${device.deviceNetworkId} 0x38 0x0101 0x0B {}"
}
def setOpenLevelTo(cValue) {
def cX = cValue
log.debug "Setting Open Light Level to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x402 0x23 {${Integer.toHexString(cX)}}"
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x402" // Read light value
cmd
}
def setCloseLevelTo(cValue) {
def cX = cValue
log.debug "Setting Close Light Level to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x401 0x23 {${Integer.toHexString(cX)}}"
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x401" // Read light value
cmd
}
def setSensitivityLevel(cValue) {
def cX = 100 - cValue
log.debug "Setting Door sensitivity level to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x408 0x23 {${Integer.toHexString(cX)}}" // Write attribute. 0x23 is a 32 bit integer value.
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x408" // Read attribute
cmd
}
def setNewBaseCurrent(cValue) {
def cX = cValue as int
log.info "Setting new BaseCurrent to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x409 0x23 {${Integer.toHexString(cX)}}" // Write attribute. 0x23 is a 32 bit integer value.
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x409" // Read attribute
cmd
}
def setNewPhotoCalibration(cValue) {
def cX = cValue as int
log.info "Setting new Photoresister calibration to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40D 0x2B {${Integer.toHexString(cX)}}" // Write attribute. 0x2B is a 32 bit signed integer value.
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40D" // Read attribute
cmd
}
def readNewPhotoCalibration() {
log.info "Requesting current Photoresister calibration "
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40D" // Read attribute
cmd
}
def readBaseCurrentNE() {
log.info "Requesting base current never exceed setting "
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40E" // Read attribute
cmd
}
def setBaseCurrentNE(cValue) {
def cX = cValue as int
log.info "Setting new base Current Never Exceed to ${cX} Hex = 0x${Integer.toHexString(cX)}"
def cmd = []
cmd << "st wattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40E 0x23 {${Integer.toHexString(cX)}}" // Write attribute. 0x23 is a 32 bit unsigned integer value.
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x40E" // Read attribute
cmd
}
def poll(){
log.debug "Polling Device"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0003" // Read Door State
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0400" // Read Current Light Level
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x39 0x0402 0x0000" // Read probe 1 Temperature
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x40 0x0402 0x0000" // Read probe 2 Temperature
cmd
}
def updateTemp1() {
log.debug "Sending attribute read request for Temperature Probe1"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x39 0x0402 0x0000" // Read Current Temperature from Coop Probe 1
cmd
}
def updateTemp2() {
log.debug "Sending attribute read request for Temperature Probe2"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x40 0x0402 0x0000" // Read Current Temperature from Coop Probe 2
cmd
}
def updateSun() {
log.debug "Sending attribute read request for Sun Light Level"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0400" // Read Current Light Level
cmd
}
def updateSensitivity() {
log.debug "Sending attribute read request for door sensitivity"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0408" // Read Door sensitivity
cmd
}
def updateCloseLightLevel() {
log.debug "Sending attribute read close light level"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0401"
cmd
}
def updateOpenLightLevel() {
log.debug "Sending attribute read open light level"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0402"
cmd
}
def refresh() {
log.debug "sending refresh command"
def cmd = []
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0003" // Read Door State
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0400" // Read Current Light Level
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0401" // Read Door Close Light Level
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0402" // Read Door Open Light Level
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0403" // Read Auto Door Close Settings
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0404" // Read Auto Door Open Settings
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x39 0x0402 0x0000" // Read Current Temperature from Coop Probe 1
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0408" // Object detection sensitivity
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x40 0x0402 0x0000" // Read Current Temperature from Coop Probe 2
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x0405" // Current required to close door
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x040B" // Aux1 Status
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x040C" // Aux2 Status
cmd << "delay 150"
cmd << "st rattr 0x${device.deviceNetworkId} 0x38 0x0101 0x409" // Read Base current
cmd
}
def configure() {
log.debug "Binding SEP 0x38 DEP 0x01 Cluster 0x0101 Lock cluster to hub"
log.debug "Binding SEP 0x39 DEP 0x01 Cluster 0x0402 Temperature cluster to hub"
log.debug "Binding SEP 0x40 DEP 0x01 Cluster 0x0402 Temperature cluster to hub"
def cmd = []
cmd << "zdo bind 0x${device.deviceNetworkId} 0x38 0x01 0x0101 {${device.zigbeeId}} {}" // Bind to end point 0x38 and the lock cluster
cmd << "delay 150"
cmd << "zdo bind 0x${device.deviceNetworkId} 0x39 0x01 0x0402 {${device.zigbeeId}} {}" // Bind to end point 0x39 and the temperature cluster
cmd << "delay 150"
cmd << "zdo bind 0x${device.deviceNetworkId} 0x40 0x01 0x0402 {${device.zigbeeId}} {}" // Bind to end point 0x40 and the temperature cluster
cmd << "delay 1500"
log.info "Sending ZigBee Configuration Commands to Coop Control"
return cmd + refresh()
}

View File

@@ -274,7 +274,6 @@ private Map makeTemperatureResult(value) {
name: 'temperature', name: 'temperature',
value: "" + value, value: "" + value,
descriptionText: "${linkText} is ${value}°${temperatureScale}", descriptionText: "${linkText} is ${value}°${temperatureScale}",
unit: temperatureScale
] ]
} }

View File

@@ -254,8 +254,7 @@ private Map getTemperatureResult(value) {
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }

View File

@@ -28,7 +28,6 @@ metadata {
attribute "powerSupply", "enum", ["USB Cable", "Battery"] attribute "powerSupply", "enum", ["USB Cable", "Battery"]
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A" fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A", outClusters: "0x5A"
fingerprint deviceId: "0x2101", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x7A,0x5A"
} }
simulator { simulator {
@@ -353,7 +352,7 @@ def configure() {
motionSensitivity == "minimum" ? 0 : 64) motionSensitivity == "minimum" ? 0 : 64)
//5. report every x minutes (threshold reports don't work on battery power, default 8 mins) //5. report every x minutes (threshold reports don't work on battery power, default 8 mins)
request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: (8*60)) //association group 1 request << zwave.configurationV1.configurationSet(parameterNumber: 111, size: 4, scaledConfigurationValue: timeOptionValueMap[reportInterval] ?: 8*60) //association group 1
request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6*60*60) //association group 2 request << zwave.configurationV1.configurationSet(parameterNumber: 112, size: 4, scaledConfigurationValue: 6*60*60) //association group 2

View File

@@ -21,8 +21,6 @@ metadata {
capability "Sensor" capability "Sensor"
capability "Battery" capability "Battery"
command "configureAfterSecure"
fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A" fingerprint deviceId: "0x0701", inClusters: "0x5E,0x86,0x72,0x59,0x85,0x73,0x71,0x84,0x80,0x30,0x31,0x70,0x98,0x7A", outClusters:"0x5A"
} }

View File

@@ -11,7 +11,6 @@
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
*/ */
metadata { metadata {
definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") { definition (name: "Arrival Sensor HA", namespace: "smartthings", author: "SmartThings") {
capability "Tone" capability "Tone"
@@ -33,7 +32,7 @@ metadata {
]) ])
} }
section { section {
input "checkInterval", "enum", title: "Presence timeout (minutes)", description: "Tap to set", input "checkInterval", "enum", title: "Presence timeout (minutes)",
defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false defaultValue:"2", options: ["2", "3", "5"], displayDuringSetup: false
} }
} }
@@ -83,6 +82,7 @@ def parse(String description) {
private handleReportAttributeMessage(String description) { private handleReportAttributeMessage(String description) {
def descMap = zigbee.parseDescriptionAsMap(description) def descMap = zigbee.parseDescriptionAsMap(description)
if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) { if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
handleBatteryEvent(Integer.parseInt(descMap.value, 16)) handleBatteryEvent(Integer.parseInt(descMap.value, 16))
} }
@@ -94,7 +94,6 @@ private handleReportAttributeMessage(String description) {
* @param volts Battery voltage in .1V increments * @param volts Battery voltage in .1V increments
*/ */
private handleBatteryEvent(volts) { private handleBatteryEvent(volts) {
def descriptionText
if (volts == 0 || volts == 255) { if (volts == 0 || volts == 255) {
log.debug "Ignoring invalid value for voltage (${volts/10}V)" log.debug "Ignoring invalid value for voltage (${volts/10}V)"
} }
@@ -108,17 +107,15 @@ private handleBatteryEvent(volts) {
volts = minVolts volts = minVolts
else if (volts > maxVolts) else if (volts > maxVolts)
volts = maxVolts volts = maxVolts
def value = batteryMap[volts] def pct = batteryMap[volts]
if (value != null) { if (pct != null) {
def linkText = getLinkText(device) def linkText = getLinkText(device)
descriptionText = '{{ linkText }} battery was {{ value }}'
def eventMap = [ def eventMap = [
name: 'battery', name: 'battery',
value: value, value: pct,
descriptionText: descriptionText, descriptionText: "${linkText} battery was ${pct}%"
translatable: true
] ]
log.debug "Creating battery event for voltage=${volts/10}V: ${linkText} ${eventMap.name} is ${eventMap.value}%" log.debug "Creating battery event for voltage=${volts/10}V: ${eventMap}"
sendEvent(eventMap) sendEvent(eventMap)
} }
} }
@@ -134,19 +131,13 @@ private handlePresenceEvent(present) {
stopTimer() stopTimer()
} }
def linkText = getLinkText(device) def linkText = getLinkText(device)
def descriptionText
if ( present )
descriptionText = "{{ linkText }} has arrived"
else
descriptionText = "{{ linkText }} has left"
def eventMap = [ def eventMap = [
name: "presence", name: "presence",
value: present ? "present" : "not present", value: present ? "present" : "not present",
linkText: linkText, linkText: linkText,
descriptionText: descriptionText, descriptionText: "${linkText} has ${present ? 'arrived' : 'left'}",
translatable: true
] ]
log.debug "Creating presence event: ${device.displayName} ${eventMap.name} is ${eventMap.value}" log.debug "Creating presence event: ${eventMap}"
sendEvent(eventMap) sendEvent(eventMap)
} }

View File

@@ -1,26 +0,0 @@
# 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.
# 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=도착알림 센서
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# Events / Notifications
'''{{ linkText }} battery was {{ value }}'''.ko={{ linkText }}의 남은 배터리 {{ value }}
'''{{ linkText }} has arrived'''.ko={{ linkText }} 귀가
'''{{ linkText }} has left'''.ko={{ linkText }} 외출
#==============================================================================

View File

@@ -87,27 +87,16 @@ def beep() {
up to this long from the time you send the message to the time you hear a sound. up to this long from the time you send the message to the time you hear a sound.
*/ */
// Used source endpoint of 0x02 because we are using smartthings manufacturer specific cluster.
[ [
"raw 0xFC05 {15 0A 11 00 00 15 01}", "raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
"delay 7000", "delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}", "raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
"delay 7000", "delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}", "raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
"delay 7000", "delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}", "raw 0xFC05 {15 0A 11 00 00 15 01}",
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
"delay 7000", "delay 7000",
"raw 0xFC05 {15 0A 11 00 00 15 01}", "raw 0xFC05 {15 0A 11 00 00 15 01}"
"delay 200",
"send 0x$zigbee.deviceNetworkId 0x02 0x$zigbee.endpointId",
] ]
} }

View File

@@ -47,9 +47,6 @@ metadata {
command "everywhereJoin" command "everywhereJoin"
command "everywhereLeave" command "everywhereLeave"
command "forceOff"
command "forceOn"
} }
/** /**
@@ -67,10 +64,8 @@ metadata {
} }
standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) { standardTile("switch", "device.switch", width: 1, height: 1, canChangeIcon: true) {
state "on", label: '${name}', action: "forceOff", icon: "st.Electronics.electronics16", backgroundColor: "#79b821", nextState:"turningOff" state "off", label: '${name}', action: "switch.on", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff"
state "turningOff", label:'TURNING OFF', icon:"st.Electronics.electronics16", backgroundColor:"#ffffff" state "on", label: '${name}', action: "switch.off", icon: "st.Electronics.electronics16", backgroundColor: "#79b821"
state "off", label: '${name}', action: "forceOn", icon: "st.Electronics.electronics16", backgroundColor: "#ffffff", nextState:"turningOn"
state "turningOn", label:'TURNING ON', icon:"st.Electronics.electronics16", backgroundColor:"#79b821"
} }
valueTile("1", "device.station1", decoration: "flat", canChangeIcon: false) { valueTile("1", "device.station1", decoration: "flat", canChangeIcon: false) {
state "station1", label:'${currentValue}', action:"preset1" state "station1", label:'${currentValue}', action:"preset1"
@@ -143,22 +138,8 @@ metadata {
* one place. * one place.
* *
*/ */
def off() { def off() { onAction("off") }
if (device.currentState("switch")?.value == "on") { def on() { onAction("on") }
onAction("off")
}
}
def forceOff() {
onAction("off")
}
def on() {
if (device.currentState("switch")?.value == "off") {
onAction("on")
}
}
def forceOn() {
onAction("on")
}
def volup() { onAction("volup") } def volup() { onAction("volup") }
def voldown() { onAction("voldown") } def voldown() { onAction("voldown") }
def preset1() { onAction("1") } def preset1() { onAction("1") }
@@ -257,11 +238,11 @@ def onAction(String user, data=null) {
def actions = null def actions = null
switch (user) { switch (user) {
case "on": case "on":
boseSetPowerState(true) actions = boseSetPowerState(true)
break break
case "off": case "off":
boseSetNowPlaying(null, "STANDBY") boseSetNowPlaying(null, "STANDBY")
boseSetPowerState(false) actions = boseSetPowerState(false)
break break
case "volume": case "volume":
actions = boseSetVolume(data) actions = boseSetVolume(data)
@@ -766,16 +747,8 @@ def cb_boseSetInput(xml, input) {
*/ */
def boseSetPowerState(boolean enable) { def boseSetPowerState(boolean enable) {
log.info "boseSetPowerState(${enable})" log.info "boseSetPowerState(${enable})"
// Fix to get faster update of power status back from speaker after sending on/off queueCallback('nowPlaying', "cb_boseSetPowerState", enable ? "POWERON" : "POWEROFF")
// Instead of queuing the command to be sent after the refresh send it directly via sendHubCommand return boseRefreshNowPlaying()
// Note: This is a temporary hack that should be replaced by a re-design of the
// DTH to use sendHubCommand for all commands
sendHubCommand(bosePOST("/key", "<key state=\"press\" sender=\"Gabbo\">POWER</key>"))
sendHubCommand(bosePOST("/key", "<key state=\"release\" sender=\"Gabbo\">POWER</key>"))
sendHubCommand(boseGET("/now_playing"))
if (enable) {
queueCallback('nowPlaying', "cb_boseConfirmPowerOn", 5)
}
} }
/** /**
@@ -814,11 +787,10 @@ def cb_boseSetPowerState(xml, state) {
*/ */
def cb_boseConfirmPowerOn(xml, tries) { def cb_boseConfirmPowerOn(xml, tries) {
def result = [] def result = []
def attempt = tries as Integer log.warn "boseConfirmPowerOn() attempt #" + tries
log.warn "boseConfirmPowerOn() attempt #$attempt" if (xml.attributes()['source'] == "STANDBY" && tries > 0) {
if (xml.attributes()['source'] == "STANDBY" && attempt > 0) {
result << boseRefreshNowPlaying() result << boseRefreshNowPlaying()
queueCallback('nowPlaying', "cb_boseConfirmPowerOn", attempt-1) queueCallback('nowPlaying', "cb_boseConfirmPowerOn", tries-1)
} }
return result return result
} }

View File

@@ -105,21 +105,11 @@ def parseDescriptionAsMap(description) {
// Commands to device // Commands to device
def on() { def on() {
[ 'zcl on-off on'
'zcl on-off on',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }
def off() { def off() {
[ 'zcl on-off off'
'zcl on-off off',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }
def setLevel(value) { def setLevel(value) {

View File

@@ -89,17 +89,14 @@ def parse(String description) {
log.debug "TEMP" log.debug "TEMP"
map.name = "temperature" map.name = "temperature"
map.value = getTemperature(descMap.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale
} else if (descMap.cluster == "0201" && descMap.attrId == "0011") { } else if (descMap.cluster == "0201" && descMap.attrId == "0011") {
log.debug "COOLING SETPOINT" log.debug "COOLING SETPOINT"
map.name = "coolingSetpoint" map.name = "coolingSetpoint"
map.value = getTemperature(descMap.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale
} else if (descMap.cluster == "0201" && descMap.attrId == "0012") { } else if (descMap.cluster == "0201" && descMap.attrId == "0012") {
log.debug "HEATING SETPOINT" log.debug "HEATING SETPOINT"
map.name = "heatingSetpoint" map.name = "heatingSetpoint"
map.value = getTemperature(descMap.value) map.value = getTemperature(descMap.value)
map.unit = temperatureScale
} else if (descMap.cluster == "0201" && descMap.attrId == "001c") { } else if (descMap.cluster == "0201" && descMap.attrId == "001c") {
log.debug "MODE" log.debug "MODE"
map.name = "thermostatMode" map.name = "thermostatMode"
@@ -172,7 +169,7 @@ def setHeatingSetpoint(degrees) {
def degreesInteger = Math.round(degrees) def degreesInteger = Math.round(degrees)
log.debug "setHeatingSetpoint({$degreesInteger} ${temperatureScale})" log.debug "setHeatingSetpoint({$degreesInteger} ${temperatureScale})"
sendEvent("name": "heatingSetpoint", "value": degreesInteger, "unit": temperatureScale) sendEvent("name": "heatingSetpoint", "value": degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2) def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius * 100) + "}" "st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius * 100) + "}"
@@ -183,7 +180,7 @@ def setCoolingSetpoint(degrees) {
if (degrees != null) { if (degrees != null) {
def degreesInteger = Math.round(degrees) def degreesInteger = Math.round(degrees)
log.debug "setCoolingSetpoint({$degreesInteger} ${temperatureScale})" log.debug "setCoolingSetpoint({$degreesInteger} ${temperatureScale})"
sendEvent("name": "coolingSetpoint", "value": degreesInteger, "unit": temperatureScale) sendEvent("name": "coolingSetpoint", "value": degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2) def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius * 100) + "}" "st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius * 100) + "}"
} }

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,34 +0,0 @@
# Connected Cree LED Bulb
Works with:
* [Connected Cree LED Bulb](https://support.smartthings.com/hc/en-us/articles/204258280-Cree-Connected-LED-Bulb)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
## Capabilities
* **Actuator** - represents that a Device has commands
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Polling** - represents that poll() can be implemented for the device
* **Refresh** - _refresh()_ command for status updates
* **Switch** - can detect state (possible values: on/off)
* **Switch Level** - represents current light level, usually 0-100 in percent
* **Health Check** - indicates ability to get device health notifications
## Device Health
A Category C6 Connected Cree LED Bulb with maxReportTime of 5 mins.
Check-in interval = 12 mins
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
Pairing needs to be tried again by placing the device closer to the hub.
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following link:
* [Cree Connected LED Bulb Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/204258280-Cree-Connected-LED-Bulb)

View File

@@ -22,7 +22,6 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Switch" capability "Switch"
capability "Switch Level" capability "Switch Level"
capability "Health Check"
fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019" fingerprint profileId: "C05E", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0000,0019"
} }
@@ -85,27 +84,11 @@ def setLevel(value) {
zigbee.setLevel(value) + ["delay 500"] + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report zigbee.setLevel(value) + ["delay 500"] + zigbee.levelRefresh() //adding refresh because of ZLL bulb not conforming to send-me-a-report
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.levelRefresh()
}
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.levelRefresh() zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.onOffConfig() + zigbee.levelConfig()
}
def healthPoll() {
log.debug "healthPoll()"
def cmds = zigbee.onOffRefresh() + zigbee.levelRefresh()
cmds.each{ sendHubCommand(new physicalgraph.device.HubAction(it))}
} }
def configure() { def configure() {
unschedule() log.debug "Configuring Reporting and Bindings."
runEvery5Minutes("healthPoll") zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh()
// Device-Watch allows 2 check-in misses from device
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
zigbee.onOffRefresh() + zigbee.levelRefresh()
} }

View File

@@ -21,9 +21,7 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
fingerprint mfr:"0063", prod:"4457", deviceJoinName: "Z-Wave Wall Dimmer" fingerprint inClusters: "0x26"
fingerprint mfr:"0063", prod:"4944", deviceJoinName: "Z-Wave Wall Dimmer"
fingerprint mfr:"0063", prod:"5044", deviceJoinName: "Z-Wave Plug-In Dimmer"
} }
simulator { simulator {
@@ -44,10 +42,6 @@ metadata {
reply "200163,delay 5000,2602": "command: 2603, payload: 63" reply "200163,delay 5000,2602": "command: 2603, payload: 63"
} }
preferences {
input "ledIndicator", "enum", title: "LED Indicator", description: "Turn LED indicator... ", required: false, options:["on": "When On", "off": "When Off", "never": "Never"], defaultValue: "off"
}
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
@@ -76,28 +70,11 @@ metadata {
} }
main(["switch"]) main(["switch"])
details(["switch", "level", "refresh"]) details(["switch", "level", "indicator", "refresh"])
} }
} }
def updated(){
switch (ledIndicator) {
case "on":
indicatorWhenOn()
break
case "off":
indicatorWhenOff()
break
case "never":
indicatorNever()
break
default:
indicatorWhenOn()
break
}
}
def parse(String description) { def parse(String description) {
def result = null def result = null
if (description != "updated") { if (description != "updated") {
@@ -161,7 +138,6 @@ def zwaveEvent(physicalgraph.zwave.commands.manufacturerspecificv2.ManufacturerS
log.debug "productTypeId: ${cmd.productTypeId}" log.debug "productTypeId: ${cmd.productTypeId}"
def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId) def msr = String.format("%04X-%04X-%04X", cmd.manufacturerId, cmd.productTypeId, cmd.productId)
updateDataValue("MSR", msr) updateDataValue("MSR", msr)
updateDataValue("manufacturer", cmd.manufacturerName)
createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false]) createEvent([descriptionText: "$device.displayName MSR: $msr", isStateChange: false])
} }
@@ -225,19 +201,19 @@ def refresh() {
delayBetween(commands,100) delayBetween(commands,100)
} }
void indicatorWhenOn() { def indicatorWhenOn() {
sendEvent(name: "indicatorStatus", value: "when on", display: false) sendEvent(name: "indicatorStatus", value: "when on")
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format())) zwave.configurationV1.configurationSet(configurationValue: [1], parameterNumber: 3, size: 1).format()
} }
void indicatorWhenOff() { def indicatorWhenOff() {
sendEvent(name: "indicatorStatus", value: "when off", display: false) sendEvent(name: "indicatorStatus", value: "when off")
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format())) zwave.configurationV1.configurationSet(configurationValue: [0], parameterNumber: 3, size: 1).format()
} }
void indicatorNever() { def indicatorNever() {
sendEvent(name: "indicatorStatus", value: "never", display: false) sendEvent(name: "indicatorStatus", value: "never")
sendHubCommand(new physicalgraph.device.HubAction(zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format())) zwave.configurationV1.configurationSet(configurationValue: [2], parameterNumber: 3, size: 1).format()
} }
def invertSwitch(invert=true) { def invertSwitch(invert=true) {

View File

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

View File

@@ -20,6 +20,7 @@ metadata {
capability "Actuator" capability "Actuator"
capability "Thermostat" capability "Thermostat"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Polling"
capability "Sensor" capability "Sensor"
capability "Refresh" capability "Refresh"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
@@ -31,13 +32,13 @@ metadata {
command "switchMode" command "switchMode"
command "switchFanMode" command "switchFanMode"
attribute "thermostatSetpoint", "number" attribute "thermostatSetpoint","number"
attribute "thermostatStatus", "string" attribute "thermostatStatus","string"
attribute "maxHeatingSetpoint", "number" attribute "maxHeatingSetpoint", "number"
attribute "minHeatingSetpoint", "number" attribute "minHeatingSetpoint", "number"
attribute "maxCoolingSetpoint", "number" attribute "maxCoolingSetpoint", "number"
attribute "minCoolingSetpoint", "number" attribute "minCoolingSetpoint", "number"
attribute "deviceTemperatureUnit", "string" attribute "deviceTemperatureUnit", "number"
} }
tiles { tiles {
@@ -133,7 +134,9 @@ def refresh() {
void poll() { void poll() {
log.debug "Executing 'poll' using parent SmartApp" log.debug "Executing 'poll' using parent SmartApp"
parent.pollChild()
def results = parent.pollChild(this)
generateEvent(results) //parse received message from parent
} }
def generateEvent(Map results) { def generateEvent(Map results) {
@@ -152,11 +155,11 @@ def generateEvent(Map results) {
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
isChange = isTemperatureStateChange(device, name, value.toString()) isChange = isTemperatureStateChange(device, name, value.toString())
isDisplayed = isChange isDisplayed = isChange
event << [value: sendValue, unit: temperatureScale, isStateChange: isChange, displayed: isDisplayed] event << [value: sendValue, isStateChange: isChange, displayed: isDisplayed]
} else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") { } else if (name=="maxCoolingSetpoint" || name=="minCoolingSetpoint" || name=="maxHeatingSetpoint" || name=="minHeatingSetpoint") {
def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F def sendValue = convertTemperatureIfNeeded(value.toDouble(), "F", 1) //API return temperature value in F
sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue sendValue = location.temperatureScale == "C"? roundC(sendValue) : sendValue
event << [value: sendValue, unit: temperatureScale, displayed: false] event << [value: sendValue, displayed: false]
} else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){ } else if (name=="heatMode" || name=="coolMode" || name=="autoMode" || name=="auxHeatMode"){
isChange = isStateChange(device, name, value.toString()) isChange = isStateChange(device, name, value.toString())
event << [value: value.toString(), isStateChange: isChange, displayed: false] event << [value: value.toString(), isStateChange: isChange, displayed: false]
@@ -234,9 +237,9 @@ void setHeatingSetpoint(setpoint) {
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite" def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) { if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}" log.debug "Done setHeatingSetpoint> coolingSetpoint: ${coolingSetpoint}, heatingSetpoint: ${heatingSetpoint}"
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
@@ -271,9 +274,9 @@ void setCoolingSetpoint(setpoint) {
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite" def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) { if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
sendEvent("name":"heatingSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"heatingSetpoint", "value":heatingSetpoint)
sendEvent("name":"coolingSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"coolingSetpoint", "value":coolingSetpoint)
log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}" log.debug "Done setCoolingSetpoint>> coolingSetpoint = ${coolingSetpoint}, heatingSetpoint = ${heatingSetpoint}"
generateSetpointEvent() generateSetpointEvent()
generateStatusEvent() generateStatusEvent()
@@ -287,14 +290,14 @@ void resumeProgram() {
log.debug "resumeProgram() is called" log.debug "resumeProgram() is called"
sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false) sendEvent("name":"thermostatStatus", "value":"resuming schedule", "description":statusText, displayed: false)
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.resumeProgram(deviceId)) { if (parent.resumeProgram(this, deviceId)) {
sendEvent("name":"thermostatStatus", "value":"setpoint is updating", "description":statusText, displayed: false) sendEvent("name":"thermostatStatus", "value":"setpoint is updating", "description":statusText, displayed: false)
runIn(5, "poll") runIn(5, "poll")
log.debug "resumeProgram() is done" log.debug "resumeProgram() is done"
sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true) sendEvent("name":"resumeProgram", "value":"resume", descriptionText: "resumeProgram is done", displayed: false, isStateChange: true)
} else { } else {
sendEvent("name":"thermostatStatus", "value":"failed resume click refresh", "description":statusText, displayed: false) sendEvent("name":"thermostatStatus", "value":"failed resume click refresh", "description":statusText, displayed: false)
log.error "Error resumeProgram() check parent.resumeProgram(deviceId)" log.error "Error resumeProgram() check parent.resumeProgram(this, deviceId)"
} }
} }
@@ -379,16 +382,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) {
@@ -406,7 +405,7 @@ def generateOperatingStateEvent(operatingState) {
def off() { def off() {
log.debug "off" log.debug "off"
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("off", deviceId)) if (parent.setMode (this,"off", deviceId))
generateModeEvent("off") generateModeEvent("off")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -420,7 +419,7 @@ def off() {
def heat() { def heat() {
log.debug "heat" log.debug "heat"
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("heat", deviceId)) if (parent.setMode (this,"heat", deviceId))
generateModeEvent("heat") generateModeEvent("heat")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -438,7 +437,7 @@ def emergencyHeat() {
def auxHeatOnly() { def auxHeatOnly() {
log.debug "auxHeatOnly" log.debug "auxHeatOnly"
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("auxHeatOnly", deviceId)) if (parent.setMode (this,"auxHeatOnly", deviceId))
generateModeEvent("auxHeatOnly") generateModeEvent("auxHeatOnly")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -452,7 +451,7 @@ def auxHeatOnly() {
def cool() { def cool() {
log.debug "cool" log.debug "cool"
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("cool", deviceId)) if (parent.setMode (this,"cool", deviceId))
generateModeEvent("cool") generateModeEvent("cool")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -466,7 +465,7 @@ def cool() {
def auto() { def auto() {
log.debug "auto" log.debug "auto"
def deviceId = device.deviceNetworkId.split(/\./).last() def deviceId = device.deviceNetworkId.split(/\./).last()
if (parent.setMode ("auto", deviceId)) if (parent.setMode (this,"auto", deviceId))
generateModeEvent("auto") generateModeEvent("auto")
else { else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -489,7 +488,7 @@ def fanOn() {
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
if (parent.setFanMode(heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) { if (parent.setFanMode(this, heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
generateFanModeEvent(fanMode) generateFanModeEvent(fanMode)
} else { } else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -510,7 +509,7 @@ def fanAuto() {
def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint def coolingValue = location.temperatureScale == "C"? convertCtoF(coolingSetpoint) : coolingSetpoint
def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint def heatingValue = location.temperatureScale == "C"? convertCtoF(heatingSetpoint) : heatingSetpoint
if (parent.setFanMode(heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) { if (parent.setFanMode(this, heatingValue, coolingValue, deviceId, sendHoldType, fanMode)) {
generateFanModeEvent(fanMode) generateFanModeEvent(fanMode)
} else { } else {
log.debug "Error setting new mode." log.debug "Error setting new mode."
@@ -556,12 +555,12 @@ def generateSetpointEvent() {
if (mode == "heat") { if (mode == "heat") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint )
} }
else if (mode == "cool") { else if (mode == "cool") {
sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":coolingSetpoint)
} else if (mode == "auto") { } else if (mode == "auto") {
@@ -573,7 +572,7 @@ def generateSetpointEvent() {
} else if (mode == "auxHeatOnly") { } else if (mode == "auxHeatOnly") {
sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint, "unit":location.temperatureScale) sendEvent("name":"thermostatSetpoint", "value":heatingSetpoint)
} }
@@ -608,7 +607,7 @@ void raiseSetpoint() {
targetvalue = maxCoolingSetpoint targetvalue = maxCoolingSetpoint
} }
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
log.info "In mode $mode raiseSetpoint() to $targetvalue" log.info "In mode $mode raiseSetpoint() to $targetvalue"
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
@@ -644,7 +643,7 @@ void lowerSetpoint() {
targetvalue = minCoolingSetpoint targetvalue = minCoolingSetpoint
} }
sendEvent("name":"thermostatSetpoint", "value":targetvalue, "unit":location.temperatureScale, displayed: false) sendEvent("name":"thermostatSetpoint", "value":targetvalue, displayed: false)
log.info "In mode $mode lowerSetpoint() to $targetvalue" log.info "In mode $mode lowerSetpoint() to $targetvalue"
runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite runIn(3, "alterSetpoint", [data: [value:targetvalue], overwrite: true]) //when user click button this runIn will be overwrite
@@ -655,60 +654,55 @@ void lowerSetpoint() {
void alterSetpoint(temp) { void alterSetpoint(temp) {
def mode = device.currentValue("thermostatMode") def mode = device.currentValue("thermostatMode")
def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint")
def deviceId = device.deviceNetworkId.split(/\./).last()
if (mode == "off" || mode == "auto") { def targetHeatingSetpoint
log.warn "this mode: $mode does not allow alterSetpoint" def targetCoolingSetpoint
} else {
def heatingSetpoint = device.currentValue("heatingSetpoint")
def coolingSetpoint = device.currentValue("coolingSetpoint")
def deviceId = device.deviceNetworkId.split(/\./).last()
def targetHeatingSetpoint //step1: check thermostatMode, enforce limits before sending request to cloud
def targetCoolingSetpoint if (mode == "heat" || mode == "auxHeatOnly"){
if (temp.value > coolingSetpoint){
//step1: check thermostatMode, enforce limits before sending request to cloud targetHeatingSetpoint = temp.value
if (mode == "heat" || mode == "auxHeatOnly"){ targetCoolingSetpoint = temp.value
if (temp.value > coolingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = coolingSetpoint
}
} else if (mode == "cool") {
//enforce limits before sending request to cloud
if (temp.value < heatingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = heatingSetpoint
targetCoolingSetpoint = temp.value
}
}
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " +
"coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
if (parent.setHold(heatingValue, coolingValue, deviceId, sendHoldType)) {
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint, "unit": location.temperatureScale)
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint, "unit": location.temperatureScale)
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
} else { } else {
log.error "Error alterSetpoint()" targetHeatingSetpoint = temp.value
if (mode == "heat" || mode == "auxHeatOnly"){ targetCoolingSetpoint = coolingSetpoint
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false) }
} else if (mode == "cool") { } else if (mode == "cool") {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false) //enforce limits before sending request to cloud
} if (temp.value < heatingSetpoint){
targetHeatingSetpoint = temp.value
targetCoolingSetpoint = temp.value
} else {
targetHeatingSetpoint = heatingSetpoint
targetCoolingSetpoint = temp.value
} }
generateStatusEvent()
} }
log.debug "alterSetpoint >> in mode ${mode} trying to change heatingSetpoint to $targetHeatingSetpoint " +
"coolingSetpoint to $targetCoolingSetpoint with holdType : ${holdType}"
def sendHoldType = holdType ? (holdType=="Temporary")? "nextTransition" : (holdType=="Permanent")? "indefinite" : "indefinite" : "indefinite"
def coolingValue = location.temperatureScale == "C"? convertCtoF(targetCoolingSetpoint) : targetCoolingSetpoint
def heatingValue = location.temperatureScale == "C"? convertCtoF(targetHeatingSetpoint) : targetHeatingSetpoint
if (parent.setHold(this, heatingValue, coolingValue, deviceId, sendHoldType)) {
sendEvent("name": "thermostatSetpoint", "value": temp.value, displayed: false)
sendEvent("name": "heatingSetpoint", "value": targetHeatingSetpoint)
sendEvent("name": "coolingSetpoint", "value": targetCoolingSetpoint)
log.debug "alterSetpoint in mode $mode succeed change setpoint to= ${temp.value}"
} else {
log.error "Error alterSetpoint()"
if (mode == "heat" || mode == "auxHeatOnly"){
sendEvent("name": "thermostatSetpoint", "value": heatingSetpoint.toString(), displayed: false)
} else if (mode == "cool") {
sendEvent("name": "thermostatSetpoint", "value": coolingSetpoint.toString(), displayed: false)
}
}
generateStatusEvent()
} }
def generateStatusEvent() { def generateStatusEvent() {

View File

@@ -21,7 +21,6 @@ metadata {
attribute "tamper", "enum", ["detected", "clear"] attribute "tamper", "enum", ["detected", "clear"]
attribute "heatAlarm", "enum", ["overheat detected", "clear", "rapid temperature rise", "underheat detected"] attribute "heatAlarm", "enum", ["overheat detected", "clear", "rapid temperature rise", "underheat detected"]
fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x9C, 0x98, 0x7A", outClusters: "0x20, 0x8B" fingerprint deviceId: "0x0701", inClusters: "0x5E, 0x86, 0x72, 0x5A, 0x59, 0x85, 0x73, 0x84, 0x80, 0x71, 0x56, 0x70, 0x31, 0x8E, 0x22, 0x9C, 0x98, 0x7A", outClusters: "0x20, 0x8B"
fingerprint mfr:"010F", prod:"0C02", model:"1002"
} }
simulator { simulator {
//battery //battery

View File

@@ -682,7 +682,7 @@ def setHeatingSetpoint(degrees) {
def temperatureScale = getTemperatureScale() def temperatureScale = getTemperatureScale()
def degreesInteger = degrees as Integer def degreesInteger = degrees as Integer
sendEvent("name":"heatingSetpoint", "value":degreesInteger, "unit":temperatureScale) sendEvent("name":"heatingSetpoint", "value":degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2) def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius*100) + "}" "st wattr 0x${device.deviceNetworkId} 1 0x201 0x12 0x29 {" + hex(celsius*100) + "}"
@@ -691,7 +691,7 @@ def setHeatingSetpoint(degrees) {
def setCoolingSetpoint(degrees) { def setCoolingSetpoint(degrees) {
def degreesInteger = degrees as Integer def degreesInteger = degrees as Integer
sendEvent("name":"coolingSetpoint", "value":degreesInteger, "unit":temperatureScale) sendEvent("name":"coolingSetpoint", "value":degreesInteger)
def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2) def celsius = (getTemperatureScale() == "C") ? degreesInteger : (fahrenheitToCelsius(degreesInteger) as Double).round(2)
"st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius*100) + "}" "st wattr 0x${device.deviceNetworkId} 1 0x201 0x11 0x29 {" + hex(celsius*100) + "}"

View File

@@ -1,7 +1,7 @@
/** /**
* GE Link Bulb * GE Link Bulb
* *
* Copyright 2016 SmartThings * Copyright 2014 SmartThings
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except * 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: * in compliance with the License. You may obtain a copy of the License at:
@@ -53,8 +53,6 @@ metadata {
capability "Polling" capability "Polling"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0019", manufacturer: "GE_Appliances", model: "ZLL Light", deviceJoinName: "GE Link Bulb" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0019", manufacturer: "GE_Appliances", model: "ZLL Light", deviceJoinName: "GE Link Bulb"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0019", manufacturer: "GE", model: "SoftWhite", deviceJoinName: "GE Link Soft White Bulb"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,1000", outClusters: "0019", manufacturer: "GE", model: "Daylight", deviceJoinName: "GE Link Daylight Bulb"
} }
// UI tile definitions // UI tile definitions

View File

@@ -1,177 +0,0 @@
/**
* Hue Bloom
*
* Philips Hue Type "Color Light"
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue Bloom", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Control"
capability "Switch"
capability "Refresh"
capability "Sensor"
capability "Health Check"
command "setAdjustedColor"
command "reset"
command "refresh"
}
simulator {
// TODO: define status and reply messages here
}
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"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset To White", action:"reset", icon:"st.lights.philips.hue-single"
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["rich-control"])
details(["rich-control", "reset", "refresh"])
}
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
void on() {
log.trace parent.on(this)
}
void off() {
log.trace parent.off(this)
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) {
log.trace parent.setLevel(this, percent)
}
}
void setSaturation(percent) {
log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) {
log.trace parent.setSaturation(this, percent)
}
}
void setHue(percent) {
log.debug "Executing 'setHue'"
if (verifyPercent(percent)) {
log.trace parent.setHue(this, percent)
}
}
void setColor(value) {
def events = []
def validValues = [:]
if (verifyPercent(value.hue)) {
validValues.hue = value.hue
}
if (verifyPercent(value.saturation)) {
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
validValues.switch = "off"
} else {
validValues.switch = "on"
}
if (!validValues.isEmpty()) {
log.trace parent.setColor(this, validValues)
}
}
void reset() {
log.debug "Executing 'reset'"
def value = [hue:20, saturation:2]
setAdjustedColor(value)
}
void setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
} else {
log.warn "Invalid color input $value"
}
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}
def verifyPercent(percent) {
if (percent == null)
return false
else if (percent >= 0 && percent <= 100) {
return true
} else {
log.warn "$percent is not 0-100"
return false
}
}
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -7,15 +7,8 @@
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") { definition (name: "Hue Bridge", namespace: "smartthings", author: "SmartThings") {
capability "Health Check" attribute "serialNumber", "string"
attribute "networkAddress", "string" attribute "networkAddress", "string"
// Used to indicate if bridge is reachable or not, i.e. is the bridge connected to the network
// Possible values "Online" or "Offline"
attribute "status", "string"
// Id is the number on the back of the hub, Hue uses last six digits of Mac address
// This is also used in the Hue application as ID
attribute "idNumber", "string"
} }
simulator { simulator {
@@ -24,30 +17,28 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"rich-control"){ multiAttributeTile(name:"rich-control"){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "Offline", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#ffffff" attributeState "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#F3C200"
attributeState "Online", label: '${currentValue}', action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#79b821"
} }
tileAttribute ("serialNumber", key: "SECONDARY_CONTROL") {
attributeState "default", label:'SN: ${currentValue}'
} }
valueTile("doNotRemove", "v", decoration: "flat", height: 2, width: 6, inactiveLabel: false) { }
state "default", label:'If removed, Hue lights will not work properly' standardTile("icon", "icon", width: 1, height: 1, canChangeIcon: false, inactiveLabel: true, canChangeBackground: false) {
state "default", label: "Hue Bridge", action: "", icon: "st.Lighting.light99-hue", backgroundColor: "#FFFFFF"
} }
valueTile("idNumber", "device.idNumber", decoration: "flat", height: 2, width: 6, inactiveLabel: false) { valueTile("serialNumber", "device.serialNumber", decoration: "flat", height: 1, width: 2, inactiveLabel: false) {
state "default", label:'ID: ${currentValue}' state "default", label:'SN: ${currentValue}'
} }
valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 6, inactiveLabel: false) { valueTile("networkAddress", "device.networkAddress", decoration: "flat", height: 2, width: 4, inactiveLabel: false) {
state "default", label:'IP: ${currentValue}' state "default", label:'${currentValue}', height: 1, width: 2, inactiveLabel: false
} }
main (["rich-control"]) main (["icon"])
details(["rich-control", "doNotRemove", "idNumber", "networkAddress"]) details(["rich-control", "networkAddress"])
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan"], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "Parsing '${description}'" log.debug "Parsing '${description}'"
@@ -68,7 +59,7 @@ def parse(description) {
log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value" log.trace "HUE BRIDGE, GENERATING EVENT: $map.name: $map.value"
results << createEvent(name: "${map.name}", value: "${map.value}") results << createEvent(name: "${map.name}", value: "${map.value}")
} else { } else {
log.trace "Parsing description" log.trace "Parsing description"
def msg = parseLanMessage(description) def msg = parseLanMessage(description)
if (msg.body) { if (msg.body) {
def contentType = msg.headers["Content-Type"] def contentType = msg.headers["Content-Type"]
@@ -76,17 +67,17 @@ def parse(description) {
def bulbs = new groovy.json.JsonSlurper().parseText(msg.body) def bulbs = new groovy.json.JsonSlurper().parseText(msg.body)
if (bulbs.state) { if (bulbs.state) {
log.info "Bridge response: $msg.body" log.info "Bridge response: $msg.body"
} else {
// Sending Bulbs List to parent"
if (parent.state.inBulbDiscovery)
log.info parent.bulbListHandler(device.hub.id, msg.body)
} }
} else if (contentType?.contains("xml")) { }
else if (contentType?.contains("xml")) {
log.debug "HUE BRIDGE ALREADY PRESENT" log.debug "HUE BRIDGE ALREADY PRESENT"
parent.hubVerification(device.hub.id, msg.body)
} }
} }
} }
} }
results results
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -1,11 +1,8 @@
/** /**
* Hue Bulb * Hue Bulb
* *
* Philips Hue Type "Extended Color Light"
*
* Author: SmartThings * Author: SmartThings
*/ */
// for the UI // for the UI
metadata { metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
@@ -17,7 +14,6 @@ metadata {
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
command "setAdjustedColor" command "setAdjustedColor"
command "reset" command "reset"
@@ -39,47 +35,67 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
} }
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
tileAttribute ("device.color", key: "COLOR_CONTROL") { tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor" attributeState "color", action:"setAdjustedColor"
} }
} }
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") { standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "colorTemperature", action:"color temperature.setColorTemperature" 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"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) { controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2000..6500)") {
state "colorTemperature", label: 'WHITES' state "colorTemperature", action:"color temperature.setColorTemperature"
} }
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: '${currentValue} K'
}
standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") { standardTile("reset", "device.reset", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"Reset To White", action:"reset", icon:"st.lights.philips.hue-single" 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" 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(["rich-control"]) main(["switch"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"]) details(["rich-control", "colorTempSliderControl", "colorTemp", "reset", "refresh"])
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
def results = [] def results = []
def map = description def map = description
if (description instanceof String) { if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}" log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description) map = stringToMap(description)
} }
if (map?.name && map?.value) { if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}") results << createEvent(name: "${map?.name}", value: "${map?.value}")
} }
@@ -89,105 +105,97 @@ def parse(description) {
// handle commands // handle commands
void on() { void on() {
log.trace parent.on(this) log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
} }
void off() { void off() {
log.trace parent.off(this) log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
void nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(level)
} }
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (verifyPercent(percent)) { parent.setLevel(this, percent)
log.trace parent.setLevel(this, percent) sendEvent(name: "level", value: percent)
}
} }
void setSaturation(percent) { void setSaturation(percent) {
log.debug "Executing 'setSaturation'" log.debug "Executing 'setSaturation'"
if (verifyPercent(percent)) { parent.setSaturation(this, percent)
log.trace parent.setSaturation(this, percent) sendEvent(name: "saturation", value: percent)
}
} }
void setHue(percent) { void setHue(percent) {
log.debug "Executing 'setHue'" log.debug "Executing 'setHue'"
if (verifyPercent(percent)) { parent.setHue(this, percent)
log.trace parent.setHue(this, percent) sendEvent(name: "hue", value: percent)
}
} }
void setColor(value) { void setColor(value) {
def events = [] log.debug "setColor: ${value}, $this"
def validValues = [:] parent.setColor(this, value)
if (value.hue) { sendEvent(name: "hue", value: value.hue)}
if (verifyPercent(value.hue)) { if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
validValues.hue = value.hue if (value.hex) { sendEvent(name: "color", value: value.hex)}
} if (value.level) { sendEvent(name: "level", value: value.level)}
if (verifyPercent(value.saturation)) { if (value.switch) { sendEvent(name: "switch", value: value.switch)}
validValues.saturation = value.saturation
}
if (value.hex != null) {
if (value.hex ==~ /^\#([A-Fa-f0-9]){6}$/) {
validValues.hex = value.hex
} else {
log.warn "$value.hex is not a valid color"
}
}
if (verifyPercent(value.level)) {
validValues.level = value.level
}
if (value.switch == "off" || (value.level != null && value.level <= 0)) {
validValues.switch = "off"
} else {
validValues.switch = "on"
}
if (!validValues.isEmpty()) {
log.trace parent.setColor(this, validValues)
}
} }
void reset() { void reset() {
log.debug "Executing 'reset'" log.debug "Executing 'reset'"
setColorTemperature(4000) def value = [level:100, hex:"#90C638", saturation:56, hue:23]
setAdjustedColor(value)
parent.poll()
} }
void setAdjustedColor(value) { void setAdjustedColor(value) {
if (value) { if (value) {
log.trace "setAdjustedColor: ${value}" log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:] def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100 // Needed because color picker always sends 100
adjusted.level = null adjusted.level = null
setColor(adjusted) setColor(adjusted)
} else {
log.warn "Invalid color input $value"
} }
} }
void setColorTemperature(value) { void setColorTemperature(value) {
if (value) { if (value) {
log.trace "setColorTemperature: ${value}k" log.trace "setColorTemperature: ${value}k"
log.trace parent.setColorTemperature(this, value) parent.setColorTemperature(this, value)
} else { sendEvent(name: "colorTemperature", value: value)
log.warn "Invalid color temperature $value" }
}
} }
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent?.manualRefresh() parent.manualRefresh()
} }
def verifyPercent(percent) { def adjustOutgoingHue(percent) {
if (percent == null) def adjusted = percent
return false if (percent > 31) {
else if (percent >= 0 && percent <= 100) { if (percent < 63.0) {
return true adjusted = percent + (7 * (percent -30 ) / 32)
} else { }
log.warn "$percent is not 0-100" else if (percent < 73.0) {
return false adjusted = 69 + (5 * (percent - 62) / 10)
} }
} else {
adjusted = percent + (2 * (100 - percent) / 28)
def ping() { }
log.trace "${parent.ping(this)}" }
log.info "percent: $percent, adjusted: $adjusted"
adjusted
} }

View File

@@ -1,8 +1,6 @@
/** /**
* Hue Lux Bulb * Hue Lux Bulb
* *
* Philips Hue Type "Dimmable Light"
*
* Author: SmartThings * Author: SmartThings
*/ */
// for the UI // for the UI
@@ -11,10 +9,10 @@ metadata {
definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") { definition (name: "Hue Lux Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level" capability "Switch Level"
capability "Actuator" capability "Actuator"
capability "Color Temperature"
capability "Switch" capability "Switch"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
command "refresh" command "refresh"
} }
@@ -34,6 +32,16 @@ metadata {
tileAttribute ("device.level", key: "SLIDER_CONTROL") { tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)" attributeState "level", action:"switch level.setLevel", range:"(0..100)"
} }
tileAttribute ("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", label: 'Level ${currentValue}%'
}
}
standardTile("switch", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "on", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "off", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
state "turningOn", label:'${name}', action:"switch.off", icon:"st.lights.philips.hue-single", backgroundColor:"#79b821", nextState:"turningOff"
state "turningOff", label:'${name}', action:"switch.on", icon:"st.lights.philips.hue-single", backgroundColor:"#ffffff", nextState:"turningOn"
} }
controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") { controlTile("levelSliderControl", "device.level", "slider", height: 1, width: 2, inactiveLabel: false, range:"(0..100)") {
@@ -44,15 +52,11 @@ metadata {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh" state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
} }
main(["rich-control"]) main(["switch"])
details(["rich-control", "refresh"]) details(["rich-control", "colorTempSliderControl","refresh"])
} }
} }
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
}
// parse events into attributes // parse events into attributes
def parse(description) { def parse(description) {
log.debug "parse() - $description" log.debug "parse() - $description"
@@ -72,27 +76,22 @@ def parse(description) {
// handle commands // handle commands
void on() { void on() {
log.trace parent.on(this) parent.on(this)
sendEvent(name: "switch", value: "on")
} }
void off() { void off() {
log.trace parent.off(this) parent.off(this)
sendEvent(name: "switch", value: "off")
} }
void setLevel(percent) { void setLevel(percent) {
log.debug "Executing 'setLevel'" log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) { parent.setLevel(this, percent)
parent.setLevel(this, percent) sendEvent(name: "level", value: percent)
} else {
log.warn "$percent is not 0-100"
}
} }
void refresh() { void refresh() {
log.debug "Executing 'refresh'" log.debug "Executing 'refresh'"
parent.manualRefresh() parent.manualRefresh()
} }
def ping() {
log.debug "${parent.ping(this)}"
}

View File

@@ -1,112 +0,0 @@
/**
* Hue White Ambiance Bulb
*
* Philips Hue Type "Color Temperature Light"
*
* Author: SmartThings
*/
// for the UI
metadata {
// Automatically generated. Make future change here.
definition (name: "Hue White Ambiance Bulb", namespace: "smartthings", author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Temperature"
capability "Switch"
capability "Refresh"
capability "Health Check"
command "refresh"
}
simulator {
// TODO: define status and reply messages here
}
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"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", range:"(0..100)"
}
}
controlTile("colorTempSliderControl", "device.colorTemperature", "slider", width: 4, height: 2, inactiveLabel: false, range:"(2200..6500)") {
state "colorTemperature", action:"color temperature.setColorTemperature"
}
valueTile("colorTemp", "device.colorTemperature", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "colorTemperature", label: 'WHITES'
}
standardTile("refresh", "device.refresh", height: 2, width: 2, inactiveLabel: false, decoration: "flat") {
state "default", label:"", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main(["rich-control"])
details(["rich-control", "colorTempSliderControl", "colorTemp", "refresh"])
}
}
void installed() {
sendEvent(name: "checkInterval", value: 60 * 12, data: [protocol: "lan", hubHardwareId: device.hub.hardwareID], displayed: false)
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Ambience Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
void on() {
log.trace parent.on(this)
}
void off() {
log.trace parent.off(this)
}
void setLevel(percent) {
log.debug "Executing 'setLevel'"
if (percent != null && percent >= 0 && percent <= 100) {
log.trace parent.setLevel(this, percent)
} else {
log.warn "$percent is not 0-100"
}
}
void setColorTemperature(value) {
if (value) {
log.trace "setColorTemperature: ${value}k"
log.trace parent.setColorTemperature(this, value)
} else {
log.warn "Invalid color temperature"
}
}
void refresh() {
log.debug "Executing 'refresh'"
parent.manualRefresh()
}
def ping() {
log.debug "${parent.ping(this)}"
}

View File

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

View File

@@ -1,19 +1,16 @@
/* /**
* Copyright 2016 SmartThings * Copyright 2015 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
* use this file except in compliance with the License. You may obtain a copy * 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
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * for the specific language governing permissions and limitations under the License.
* License for the specific language governing permissions and limitations *
* under the License.
*/ */
metadata { metadata {
definition (name: "Mobile Presence", namespace: "smartthings", author: "SmartThings") { definition (name: "Mobile Presence", namespace: "smartthings", author: "SmartThings") {
capability "Presence Sensor" capability "Presence Sensor"
@@ -44,7 +41,6 @@ def parse(String description) {
def isStateChange = isStateChange(device, name, value) def isStateChange = isStateChange(device, name, value)
def results = [ def results = [
translatable: true,
name: name, name: name,
value: value, value: value,
unit: null, unit: null,
@@ -76,8 +72,8 @@ private String parseValue(String description) {
private parseDescriptionText(String linkText, String value, String description) { private parseDescriptionText(String linkText, String value, String description) {
switch(value) { switch(value) {
case "present": return "{{ linkText }} has arrived" case "present": return "$linkText has arrived"
case "not present": return "{{ linkText }} has left" case "not present": return "$linkText has left"
default: return value default: return value
} }
} }

View File

@@ -13,7 +13,6 @@
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "NYCE Motion Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "NYCE Motion Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -144,14 +143,51 @@ private Map parseReportAttributeMessage(String description) {
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
Map resultMap = [:] String msgCode = parsedMsg[2]
resultMap.name = 'motion' Map resultMap = [:]
resultMap.value = zs.isAlarm2Set() ? 'active' : 'inactive' switch(msgCode) {
log.debug(zs.isAlarm2Set() ? 'motion' : 'no motion') case '0x0030': // Closed/No Motion/Dry
log.debug 'no motion'
resultMap.name = 'motion'
resultMap.value = 'inactive'
break
return resultMap case '0x0032': // Open/Motion/Wet
log.debug 'motion'
resultMap.name = 'motion'
resultMap.value = 'active'
break
case '0x0032': // Tamper Alarm
log.debug 'motion with tamper alarm'
resultMap.name = 'motion'
resultMap.value = 'active'
break
case '0x0033': // Battery Alarm
break
case '0x0034': // Supervision Report
log.debug 'no motion with tamper alarm'
resultMap.name = 'motion'
resultMap.value = 'inactive'
break
case '0x0035': // Restore Report
break
case '0x0036': // Trouble/Failure
log.debug 'motion with failure alarm'
resultMap.name = 'motion'
resultMap.value = 'active'
break
case '0x0038': // Test Mode
break
}
return resultMap
} }
def refresh() def refresh()

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,37 +0,0 @@
# Nyce Door/Window Sensor (Open/Close Sensor)
Works with:
* [NYCE Door/Window Sensor NCZ-3011](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
* [Troubleshooting](#troubleshooting)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Contact Sensor** - can detect contact (with possible values - open/closed)
* **Battery** - defines device uses a battery
* **Refresh** - _refresh()_ command for status updates
* **Health Check** - indicates ability to get device health notifications
## Device Health
A Category C2 Nyce Door/Window sensor that has 12min check-in interval
## Battery Specification
One 3V CR2032 battery required.
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
* [Nyce Door/Window Sensor](https://support.smartthings.com/hc/en-us/articles/204576764-NYCE-Door-Window-Sensor)

View File

@@ -14,25 +14,20 @@
* *
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "NYCE Open/Closed Sensor", namespace: "smartthings", author: "NYCE") { definition (name: "NYCE Open/Closed Sensor", namespace: "smartthings", author: "NYCE") {
capability "Battery" capability "Battery"
capability "Configuration" capability "Configuration"
capability "Contact Sensor" capability "Contact Sensor"
capability "Refresh" capability "Refresh"
capability "Health Check"
command "enrollResponse" command "enrollResponse"
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3010", deviceJoinName: "NYCE Door Hinge Sensor"
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor" fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor" fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3011", deviceJoinName: "NYCE Door/Window Sensor"
fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" fingerprint inClusters: "0000,0001,0003,0406,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor" fingerprint inClusters: "0000,0001,0003,0500,0020", manufacturer: "NYCE", model: "3014", deviceJoinName: "NYCE Tilt Sensor"
} }
simulator { simulator {
@@ -223,33 +218,40 @@ private Map parseReportAttributeMessage(String description) {
} }
private List parseIasMessage(String description) { private List parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(" ")
log.debug "parseIasMessage: $description" String msgCode = parsedMsg[2]
List resultListMap = [] List resultListMap = []
Map resultMap_battery = [:] Map resultMap_battery = [:]
Map resultMap_battery_state = [:] Map resultMap_battery_state = [:]
Map resultMap_sensor = [:] Map resultMap_sensor = [:]
resultMap_sensor.name = "contact" // Relevant bit field definitions from ZigBee spec
resultMap_sensor.value = zs.isAlarm1Set() ? "open" : "closed" def BATTERY_BIT = ( 1 << 3 )
def TROUBLE_BIT = ( 1 << 6 )
def SENSOR_BIT = ( 1 << 0 ) // it's ALARM1 bit from the ZCL spec
// Convert hex string to integer
def zoneStatus = Integer.parseInt(msgCode[-4..-1],16)
log.debug "parseIasMessage: zoneStatus: ${zoneStatus}"
// Check each relevant bit, create map for it, and add to list // Check each relevant bit, create map for it, and add to list
log.debug "parseIasMessage: Battery Status ${zs.battery}" log.debug "parseIasMessage: Battery Status ${zoneStatus & BATTERY_BIT}"
log.debug "parseIasMessage: Trouble Status ${zs.trouble}" log.debug "parseIasMessage: Trouble Status ${zoneStatus & TROUBLE_BIT}"
log.debug "parseIasMessage: Sensor Status ${zs.alarm1}" log.debug "parseIasMessage: Sensor Status ${zoneStatus & SENSOR_BIT}"
/* Comment out this path to check the battery state to avoid overwriting the /* Comment out this path to check the battery state to avoid overwriting the
battery value (Change log #2), but keep these conditions for later use battery value (Change log #2), but keep these conditions for later use
resultMap_battery_state.name = "battery_state" resultMap_battery_state.name = "battery_state"
if (zs.isTroubleSet()) { if (zoneStatus & TROUBLE_BIT) {
resultMap_battery_state.value = "failed" resultMap_battery_state.value = "failed"
resultMap_battery.name = "battery" resultMap_battery.name = "battery"
resultMap_battery.value = 0 resultMap_battery.value = 0
} }
else { else {
if (zs.isBatterySet()) { if (zoneStatus & BATTERY_BIT) {
resultMap_battery_state.value = "low" resultMap_battery_state.value = "low"
// to generate low battery notification by the platform // to generate low battery notification by the platform
@@ -267,6 +269,9 @@ private List parseIasMessage(String description) {
} }
*/ */
resultMap_sensor.name = "contact"
resultMap_sensor.value = (zoneStatus & SENSOR_BIT) ? "open" : "closed"
resultListMap << resultMap_battery_state resultListMap << resultMap_battery_state
resultListMap << resultMap_battery resultListMap << resultMap_battery
resultListMap << resultMap_sensor resultListMap << resultMap_sensor
@@ -274,28 +279,23 @@ private List parseIasMessage(String description) {
return resultListMap return resultListMap
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def configure() { def configure() {
// Device-Watch allows 2 check-in misses from device
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
def enrollCmds = [ def configCmds = [
//battery reporting and heartbeat
"zdo bind 0x${device.deviceNetworkId} 1 ${endpointId} 1 {${device.zigbeeId}} {}", "delay 200",
"zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 1500",
// Writes CIE attribute on end device to direct reports to the hub's EUID // Writes CIE attribute on end device to direct reports to the hub's EUID
"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",
] ]
log.debug "configure: Write IAS CIE" log.debug "configure: Write IAS CIE"
// battery minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity return configCmds
return enrollCmds + zigbee.batteryConfig(30, 300) + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {
@@ -340,8 +340,7 @@ Integer convertHexToInt(hex) {
def refresh() { def refresh() {
log.debug "Refreshing Battery" log.debug "Refreshing Battery"
def refreshCmds = [ [
"st rattr 0x${device.deviceNetworkId} ${endpointId} 1 0x20", "delay 200" "st rattr 0x${device.deviceNetworkId} ${endpointId} 1 0x20", "delay 200"
] ]
return refreshCmds + enrollResponse()
} }

View File

@@ -21,6 +21,9 @@ metadata {
attribute "colorName", "string" attribute "colorName", "string"
command "setAdjustedColor" command "setAdjustedColor"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "Gardenspot RGB"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0008,0300,0B04,FC0F", outClusters: "0019", manufacturer: "OSRAM", model: "LIGHTIFY Gardenspot RGB"
} }
// simulator metadata // simulator metadata
@@ -88,7 +91,7 @@ def parse(String description) {
if (descMap.cluster == "0300") { if (descMap.cluster == "0300") {
if(descMap.attrId == "0000"){ //Hue Attribute if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100) def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
log.debug "Hue value returned is $hueValue" log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false) sendEvent(name: "hue", value: hueValue, displayed:false)
} }
@@ -200,7 +203,7 @@ def setLevel(value) {
//input Hue Integer values; returns color name for saturation 100% //input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){ private getColorName(hueValue){
if(hueValue>100 || hueValue<0) if(hueValue>360 || hueValue<0)
return return
hueValue = Math.round(hueValue / 100 * 360) hueValue = Math.round(hueValue / 100 * 360)

View File

@@ -118,7 +118,7 @@ def parse(String description) {
} }
} }
else if(descMap.attrId == "0000"){ //Hue Attribute else if(descMap.attrId == "0000"){ //Hue Attribute
def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 100) def hueValue = Math.round(convertHexToInt(descMap.value) / 255 * 360)
log.debug "Hue value returned is $hueValue" log.debug "Hue value returned is $hueValue"
sendEvent(name: "hue", value: hueValue, displayed:false) sendEvent(name: "hue", value: hueValue, displayed:false)
} }
@@ -274,7 +274,7 @@ private getGenericName(value){
//input Hue Integer values; returns color name for saturation 100% //input Hue Integer values; returns color name for saturation 100%
private getColorName(hueValue){ private getColorName(hueValue){
if(hueValue>100 || hueValue<0) if(hueValue>360 || hueValue<0)
return return
hueValue = Math.round(hueValue / 100 * 360) hueValue = Math.round(hueValue / 100 * 360)

View File

@@ -19,6 +19,11 @@ metadata {
capability "Sensor" capability "Sensor"
attribute "colorName", "string" attribute "colorName", "string"
// indicates that device keeps track of heartbeat (in state.heartbeat)
attribute "heartbeat", "string"
} }
// simulator metadata // simulator metadata
@@ -70,6 +75,9 @@ metadata {
def parse(String description) { def parse(String description) {
//log.trace description //log.trace description
// save heartbeat (i.e. last time we got a message from device)
state.heartbeat = Calendar.getInstance().getTimeInMillis()
if (description?.startsWith("catchall:")) { if (description?.startsWith("catchall:")) {
if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1")) if(description?.endsWith("0100") ||description?.endsWith("1001") || description?.matches("on/off\\s*:\\s*1"))
{ {
@@ -124,6 +132,7 @@ def off() {
} }
def refresh() { def refresh() {
sendEvent(name: "heartbeat", value: "alive", displayed:false)
[ [
"st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500", "st rattr 0x${device.deviceNetworkId} ${endpointId} 6 0", "delay 500",
"st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500", "st rattr 0x${device.deviceNetworkId} ${endpointId} 8 0", "delay 500",

View File

@@ -133,7 +133,7 @@ def refresh() {
} }
def configure() { def configure() {
refresh() + onOffConfig() + levelConfig() + powerConfig() onOffConfig() + levelConfig() + powerConfig() + refresh()
} }

View File

@@ -47,21 +47,9 @@ def parse(String description) {
// Commands to device // Commands to device
def on() { def on() {
[ 'zcl on-off on'
'zcl on-off on',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }
def off() { def off() {
[ 'zcl on-off off'
'zcl on-off off',
'delay 200',
"send 0x${zigbee.deviceNetworkId} 0x01 0x${zigbee.endpointId}",
'delay 500'
]
} }

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,38 +0,0 @@
# SmartPower Outlet
Works with:
* [Samsung SmartPower Outlet](https://shop.smartthings.com/#!/products/smartpower-outlet)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Actuator** - represents that a Device has commands
* **Switch** - can detect state (possible values: on/off)
* **Refresh** - _refresh()_ command for status updates
* **Power Meter** - detects power meter for device in either w or kw.
* **Health Check** - indicates ability to get device health notifications
* **Sensor** - detects sensor events
## Device Health
A Category C1 smart power outlet with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the device is out of range.
Pairing needs to be tried again by placing the device closer to the hub.
Instructions related to pairing, resetting and removing the device from SmartThings can be found in the following links
for the different models:
* [SmartPower Outlet Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/201084854-SmartPower-Outlet)
* [Samsung SmartThings Outlet Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957620)

View File

@@ -1,26 +0,0 @@
# 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.
# Korean (ko)
# Device Preferences
'''Give your device a name'''.ko=기기 이름 설정
'''Outlet'''.ko= 스마트 플러그
# Events descriptionText
'''{{ device.displayName }} is On'''.ko={{ device.displayName }} 켜짐
'''{{ device.displayName }} is Off'''.ko={{ device.displayName }} 꺼짐
'''{{ device.displayName }} power is {{ value }} Watts'''.ko={{ device.displayName }} 전력은 {{ value }}와트입니다.
'''On'''.ko= 켜짐
'''Off'''.ko=꺼짐
'''Turning On'''.ko=켜는 중
'''Turning Off'''.ko=끄는 중
#==============================================================================

View File

@@ -1,19 +1,20 @@
/* /**
* Copyright 2016 SmartThings * Copyright 2015 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
* use this file except in compliance with the License. You may obtain a copy * 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
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * for the specific language governing permissions and limitations under the License.
* License for the specific language governing permissions and limitations *
* under the License. * SmartPower Outlet (CentraLite)
*
* Author: SmartThings
* Date: 2015-08-23
*/ */
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") {
@@ -23,7 +24,9 @@ metadata {
capability "Configuration" capability "Configuration"
capability "Refresh" capability "Refresh"
capability "Sensor" capability "Sensor"
capability "Health Check"
// indicates that device keeps track of heartbeat (in state.heartbeat)
attribute "heartbeat", "string"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200", deviceJoinName: "Outlet"
fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet" fingerprint profileId: "0104", inClusters: "0000,0003,0004,0005,0006,0B04,0B05", outClusters: "0019", manufacturer: "CentraLite", model: "3200-Sgb", deviceJoinName: "Outlet"
@@ -55,10 +58,10 @@ metadata {
tiles(scale: 2) { tiles(scale: 2) {
multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){ multiAttributeTile(name:"switch", type: "lighting", width: 6, height: 4, canChangeIcon: true){
tileAttribute ("device.switch", key: "PRIMARY_CONTROL") { tileAttribute ("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label: 'On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff" attributeState "on", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "off", label: 'Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" attributeState "off", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
attributeState "turningOn", label: 'Turning On', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff" attributeState "turningOn", label: '${name}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821", nextState: "turningOff"
attributeState "turningOff", label: 'Turning Off', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn" attributeState "turningOff", label: '${name}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff", nextState: "turningOn"
} }
tileAttribute ("power", key: "SECONDARY_CONTROL") { tileAttribute ("power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'${currentValue} W' attributeState "power", label:'${currentValue} W'
@@ -78,6 +81,9 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
// save heartbeat (i.e. last time we got a message from device)
state.heartbeat = Calendar.getInstance().getTimeInMillis()
def finalResult = zigbee.getKnownDescription(description) def finalResult = zigbee.getKnownDescription(description)
//TODO: Remove this after getKnownDescription can parse it automatically //TODO: Remove this after getKnownDescription can parse it automatically
@@ -85,40 +91,27 @@ def parse(String description) {
finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description)) finalResult = getPowerDescription(zigbee.parseDescriptionAsMap(description))
if (finalResult) { if (finalResult) {
log.info "final result = $finalResult" log.info finalResult
if (finalResult.type == "update") { if (finalResult.type == "update") {
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 powerValue = (finalResult.value as Integer)/10
sendEvent(name: "power", value: powerValue, descriptionText: '{{ device.displayName }} power is {{ value }} Watts', translatable: true ) sendEvent(name: "power", value: powerValue)
/* /*
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
to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702 to account for the different Divisor value (AttrId: 0302) and POWER Unit (AttrId: 0300). CLUSTER for simple metering is 0702
*/ */
} }
else { else {
def descriptionText = finalResult.value == "on" ? '{{ device.displayName }} is On' : '{{ device.displayName }} is Off' sendEvent(name: finalResult.type, value: finalResult.value)
sendEvent(name: finalResult.type, value: finalResult.value, descriptionText: descriptionText, translatable: true)
} }
} }
else { else {
def cluster = zigbee.parse(description) log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug zigbee.parseDescriptionAsMap(description)
if (cluster && cluster.clusterId == 0x0006 && cluster.command == 0x07){
if (cluster.data[0] == 0x00) {
log.debug "ON/OFF REPORTING CONFIG RESPONSE: " + cluster
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
}
else {
log.warn "ON/OFF REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
log.warn "DID NOT PARSE MESSAGE for description : $description"
log.debug "${cluster}"
}
} }
} }
@@ -129,24 +122,14 @@ def off() {
def on() { def on() {
zigbee.on() zigbee.on()
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.onOffRefresh()
}
def refresh() { def refresh() {
zigbee.onOffRefresh() + zigbee.electricMeasurementPowerRefresh() sendEvent(name: "heartbeat", value: "alive", displayed:false)
zigbee.onOffRefresh() + zigbee.refreshData("0x0B04", "0x050B")
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) zigbee.onOffConfig() + powerConfig() + refresh()
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// OnOff minReportTime 0 seconds, maxReportTime 5 min. Reporting interval if no activity
refresh() + zigbee.onOffConfig(0, 300) + powerConfig()
} }
//power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s) //power config for devices with min reporting interval as 1 seconds and reporting interval if no activity as 10min (600s)

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,44 +0,0 @@
# Smartsense Moisture Sensor
Works with:
* [Samsung SmartThings Moisture Sensor](https://shop.smartthings.com/#!/products/samsung-smartthings-water-leak-sensor)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Battery** - defines device uses a battery
* **Refresh** - _refresh()_ command for status updates
* **Temperature Measurement** - defines device measures current temperature
* **Water Sensor** - can detect presence of water (dry or wet)
* **Health Check** - indicates ability to get device health notifications
## Device Health
A Category C2 moisture sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Battery Specification
One CR2 3V battery required.
## Troubleshooting
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the different sensors from SmartThings can be found in the following links
for the different models:
* [SmartSense Moisture Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202847044-SmartSense-Moisture-Sensor)
* [Samsung SmartThings Water Leak Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957630)
Other troubleshooting tips are listed as follows:
* [Troubleshooting: Samsung SmartThings Water Leak Sensor wont pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)

View File

@@ -1,35 +1,14 @@
# Copyright 2016 SmartThings
# # Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
# Licensed under the Apache License, Version 2.0 (the "License"); you may not '''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
# use this file except in compliance with the License. You may obtain a copy '''Degrees'''.ko=온도
# 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.
# Korean (ko)
# Device Preferences
'''Dry'''.ko=건조
'''Wet'''.ko=누수
'''dry'''.ko=건조
'''wet'''.ko=누수
'''battery'''.ko=배터리
'''Temperature Offset'''.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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다. '''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=온도 '''battery'''.ko=배터리
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오 '''dry'''.ko=건조
'''Give your device a name'''.ko=기기 이름 설정 '''wet'''.ko=누수
'''Water Leak Sensor'''.ko=누수감지 센서
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# 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 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 }}였습니다
#============================================================================== '''{{ device.displayName }} is {{ value | translate }}'''.ko={{ device.displayName }}이(가) {{ value | translate }}입니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -1,21 +1,18 @@
/* /**
* Copyright 2016 SmartThings * SmartSense Moisture Sensor
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * Copyright 2014 SmartThings
* 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 * 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
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * for the specific language governing permissions and limitations under the License.
* License for the specific language governing permissions and limitations *
* under the License.
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Moisture Sensor",namespace: "smartthings", author: "SmartThings") {
capability "Configuration" capability "Configuration"
@@ -23,8 +20,6 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Water Sensor" capability "Water Sensor"
capability "Health Check"
capability "Sensor"
command "enrollResponse" command "enrollResponse"
@@ -48,7 +43,7 @@ metadata {
]) ])
} }
section { 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 input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }
@@ -118,39 +113,27 @@ private Map parseCatchAllMessage(String description) {
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
// 0x07 - configure reporting resultMap = getBatteryResult(cluster.data.last())
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break break
case 0x0402: case 0x0402:
if (cluster.command == 0x07) { // temp is last 2 data values. reverse to swap endian
if (cluster.data[0] == 0x00){ String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster def value = getTemperature(temp)
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) resultMap = getTemperatureResult(value)
} break
else { }
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}" }
}
}
else {
// 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) { private boolean shouldProcessMessage(cluster) {
// 0x0B is default response indicating message got through // 0x0B is default response indicating message got through
// 0x07 is bind message
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
@@ -184,17 +167,50 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2]
return zs.isAlarm1Set() ? getMoistureResult('wet') : getMoistureResult('dry') Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getMoistureResult('dry')
break
case '0x0021': // Open/Motion/Wet
resultMap = getMoistureResult('wet')
break
case '0x0022': // Tamper Alarm
break
case '0x0023': // Battery Alarm
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 '0x0026': // Trouble/Failure
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100 def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == "C"){ if(getTemperatureScale() == "C"){
return Math.round(celsius) return celsius
} else { } else {
return Math.round(celsiusToFahrenheit(celsius)) return celsiusToFahrenheit(celsius) as Integer
} }
} }
@@ -204,8 +220,7 @@ private Map getBatteryResult(rawValue) {
def result = [ def result = [
name: 'battery', name: 'battery',
value: '--', value: '--'
translatable: true
] ]
def volts = rawValue / 10 def volts = rawValue / 10
@@ -213,7 +228,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {} if (rawValue == 0 || rawValue == 255) {}
else { else {
if (volts > 3.5) { if (volts > 3.5) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
} }
else { else {
if (device.getDataValue("manufacturer") == "SmartThings") { if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -230,18 +245,15 @@ 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 }}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
} }
else { else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0) result.descriptionText = "${linkText} battery was ${result.value}%"
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
} }
} }
} }
@@ -251,48 +263,30 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) { private Map getTemperatureResult(value) {
log.debug 'TEMP' log.debug 'TEMP'
def linkText = getLinkText(device)
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 def descriptionText = "${linkText} was ${value}°${temperatureScale}"
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
translatable: true,
unit: temperatureScale
] ]
} }
private Map getMoistureResult(value) { private Map getMoistureResult(value) {
log.debug "water" log.debug 'water'
def descriptionText String descriptionText = "${device.displayName} is ${value}"
if ( value == "wet" )
descriptionText = '{{ device.displayName }} is wet'
else
descriptionText = '{{ device.displayName }} is dry'
return [ return [
name: 'water', name: 'water',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
translatable: true
] ]
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() { def refresh() {
log.debug "Refreshing Temperature and Battery" log.debug "Refreshing Temperature and Battery"
def refreshCmds = [ def refreshCmds = [
@@ -304,13 +298,21 @@ def refresh() {
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
// enrolls with default periodic reporting until newer 5 min interval is confirmed log.debug "Configuring Reporting, IAS CIE, and Bindings."
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) def configCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
// battery minReport 30 seconds, maxReportTime 6 hrs by default "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config "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"
]
return configCmds + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,45 +0,0 @@
# Smartsense Motion Sensor
Works with:
* [Samsung SmartThings Motion Sensor](https://shop.smartthings.com/#!/products/samsung-smartthings-motion-sensor)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Motion Sensor** - can detect motion
* **Battery** - defines device uses a battery
* **Refresh** - _refresh()_ command for status updates
* **Health Check** - indicates ability to get device health notifications
## Device Health
A Category C2 motion sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Battery Specification
One CR2477 (for Samsung SmartThings Motion Sensor) / CR123A (SmartSense Motion Sensor) 3V battery is required.
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
for the different models:
* [SmartSense Motion Sensor (original model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/200903280-SmartSense-Motion-Sensor-original-model-)
* [SmartSense Motion Sensor (2014 model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/203077520-SmartSense-Motion-Sensor-2014-model-)
* [Samsung SmartThings Motion Sensor (2015 model) Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/205957580-Samsung-SmartThings-Motion-Sensor-2015-model-)
Other troubleshooting tips are listed as follows:
* [Troubleshooting: Samsung SmartThings Motion Sensor is stuck showing "Motion Detected" or "No Motion"](https://support.smartthings.com/hc/en-us/articles/200961130-Troubleshooting-Samsung-SmartThings-Motion-Sensor-is-stuck-showing-Motion-Detected-or-No-Motion-)
* [Troubleshooting: Samsung SmartThings Motion Sensor wont pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)

View File

@@ -1,33 +1,13 @@
# Copyright 2016 SmartThings
# # Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
# Licensed under the Apache License, Version 2.0 (the "License"); you may not '''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
# use this file except in compliance with the License. You may obtain a copy '''Degrees'''.ko=온도
# 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.
# Korean (ko)
# Device Preferences
'''battery'''.ko=배터리
'''Temperature Offset'''.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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다. '''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=온도 '''battery'''.ko=배터리
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Give your device a name'''.ko=기기 이름 설정
'''Motion Sensor'''.ko=모션 센서
'''motion'''.ko= 동작 감지
'''no motion'''.ko=동작 없음
'''${currentValue}% battery'''.ko=${currentValue}% 배터리
# 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 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 }}였습니다
#============================================================================== '''{{ device.displayName }} detected motion'''.ko={{ device.displayName }}가 움직임을 감지하였습니다.
'''{{ device.displayName }} motion has stopped'''.ko={{ device.displayName }} 동작이 중단되었습니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -1,20 +1,18 @@
/* /**
* Copyright 2016 SmartThings * SmartSense Motion/Temp Sensor
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * Copyright 2014 SmartThings
* 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 * 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
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * for the specific language governing permissions and limitations under the License.
* License for the specific language governing permissions and limitations *
* under the License.
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Motion Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -23,8 +21,6 @@ metadata {
capability "Battery" capability "Battery"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Refresh" capability "Refresh"
capability "Health Check"
capability "Sensor"
command "enrollResponse" command "enrollResponse"
@@ -51,7 +47,7 @@ metadata {
]) ])
} }
section { 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 input "tempOffset", "number", title: "Degrees", description: "Adjust temperature by this many degrees", range: "*..*", displayDuringSetup: false
} }
} }
@@ -122,37 +118,19 @@ private Map parseCatchAllMessage(String description) {
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
// 0x07 - configure reporting resultMap = getBatteryResult(cluster.data.last())
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break break
case 0x0402: case 0x0402:
if (cluster.command == 0x07) { // temp is last 2 data values. reverse to swap endian
if (cluster.data[0] == 0x00) { String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster def value = getTemperature(temp)
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) resultMap = getTemperatureResult(value)
}
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
// 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 break
case 0x0406: case 0x0406:
// 0x07 - configure reporting log.debug 'motion'
if (cluster.command != 0x07) { resultMap.name = 'motion'
log.debug 'motion'
resultMap.name = 'motion'
}
break break
} }
} }
@@ -162,8 +140,10 @@ 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
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
@@ -201,18 +181,52 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2]
// Some sensor models that use this DTH use alarm1 and some use alarm2 to signify motion Map resultMap = [:]
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getMotionResult('inactive')
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 '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
log.debug 'no motion with tamper alarm'
resultMap = getMotionResult('inactive')
break
case '0x0025': // Restore Report
break
case '0x0026': // Trouble/Failure
log.debug 'motion with failure alarm'
resultMap = getMotionResult('active')
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100 def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == "C"){ if(getTemperatureScale() == "C"){
return Math.round(celsius) return celsius
} else { } else {
return Math.round(celsiusToFahrenheit(celsius)) return celsiusToFahrenheit(celsius) as Integer
} }
} }
@@ -222,8 +236,7 @@ private Map getBatteryResult(rawValue) {
def result = [ def result = [
name: 'battery', name: 'battery',
value: '--', value: '--'
translatable: true
] ]
def volts = rawValue / 10 def volts = rawValue / 10
@@ -231,7 +244,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {} if (rawValue == 0 || rawValue == 255) {}
else { else {
if (volts > 3.5) { if (volts > 3.5) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
} }
else { else {
if (device.getDataValue("manufacturer") == "SmartThings") { if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -248,19 +261,15 @@ private Map getBatteryResult(rawValue) {
def pct = batteryMap[volts] def pct = batteryMap[volts]
if (pct != null) { if (pct != null) {
result.value = pct result.value = pct
def value = pct result.descriptionText = "${linkText} battery was ${result.value}%"
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
} }
} }
else { else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0) result.descriptionText = "${linkText} battery was ${result.value}%"
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
} }
} }
} }
@@ -270,44 +279,31 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) { private Map getTemperatureResult(value) {
log.debug 'TEMP' log.debug 'TEMP'
def linkText = getLinkText(device)
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 def descriptionText = "${linkText} was ${value}°${temperatureScale}"
if ( temperatureScale == 'C' )
descriptionText = '{{ device.displayName }} was {{ value }}°C'
else
descriptionText = '{{ device.displayName }} was {{ value }}°F'
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
translatable: true,
unit: temperatureScale
] ]
} }
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 linkText = getLinkText(device)
String descriptionText = value == 'active' ? "${linkText} detected motion" : "${linkText} motion has stopped"
return [ return [
name: 'motion', name: 'motion',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
translatable: true
] ]
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() { def refresh() {
log.debug "refresh called" log.debug "refresh called"
def refreshCmds = [ def refreshCmds = [
@@ -319,13 +315,22 @@ def refresh() {
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
// enrolls with default periodic reporting until newer 5 min interval is confirmed log.debug "Configuring Reporting, IAS CIE, and Bindings."
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity def configCmds = [
// battery minReport 30 seconds, maxReportTime 6 hrs by default "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config "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
"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 300 3600 {6400}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
return configCmds + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -15,7 +15,6 @@
*/ */
//DEPRECATED - Using the smartsense-motion-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH //DEPRECATED - Using the smartsense-motion-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Motion/Temp Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -169,8 +168,44 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
return (zs.isAlarm1Set() || zs.isAlarm2Set()) ? getMotionResult('active') : getMotionResult('inactive') String msgCode = parsedMsg[2]
Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getMotionResult('inactive')
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 '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
log.debug 'no motion with tamper alarm'
resultMap = getMotionResult('inactive')
break
case '0x0025': // Restore Report
break
case '0x0026': // Trouble/Failure
log.debug 'motion with failure alarm'
resultMap = getMotionResult('active')
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -205,10 +240,7 @@ private Map getBatteryResult(rawValue) {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "${linkText} battery was ${result.value}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
} }
@@ -228,8 +260,7 @@ private Map getTemperatureResult(value) {
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }
@@ -255,9 +286,13 @@ def refresh() {
} }
def configure() { def configure() {
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",
"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}", //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",
@@ -266,7 +301,7 @@ def configure() {
"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 ${endpointId}", "delay 500" "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
] ]
return refresh() + configCmds // send refresh cmds as part of config return configCmds + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,45 +0,0 @@
# Smartsense Multi Sensor
Works with:
* [Samsung SmartThings Multi Sensor](https://shop.smartthings.com/#!/products/smartsense-multi)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Three Axis** - monitors the state of a single axis
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Battery** - defines device uses a battery
* **Sensor** - detects sensor events
* **Contact Sensor** - can detect contact (possible values: open,closed)
* **Acceleration Sensor** - allows for acceleration detection.
* **Refresh** - _refresh()_ command for status updates
* **Temperature Measurement** - defines device measures current temperature
* **Health Check** - indicates ability to get device health notifications
## Device Health
A Category C2 multi sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Battery Specification
One CR2450 (for Samsung SmartThings Multipurpose Sensor) battery / Two AAAA (for SmartSense Multi Sensor) batteries required.
## Troubleshooting
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Other troubleshooting tips are listed as follows:
* [Troubleshooting: Samsung SmartThings Multipurpose Sensor is stuck on "open" or "closed"](https://support.smartthings.com/hc/en-us/articles/200955940-Troubleshooting-Samsung-SmartThings-Multipurpose-Sensor-is-stuck-on-open-or-closed-)
* [Troubleshooting: Temperature reading for the Samsung SmartThings Multipurpose Sensor is off](https://support.smartthings.com/hc/en-us/articles/200756845-Troubleshooting-Temperature-reading-for-the-Samsung-SmartThings-Multipurpose-Sensor-is-off)
* [Troubleshooting: Samsung SmartThings Multipurpose Sensor wont pair after removing pull-tab](https://support.smartthings.com/hc/en-us/articles/204966616-Troubleshooting-Samsung-SmartThings-device-won-t-pair-after-removing-pull-tab)

View File

@@ -1,41 +1,20 @@
# Copyright 2016 SmartThings
# # Generated on Wed Feb 24 14:28:26 CST 2016 by dylan
# Licensed under the Apache License, Version 2.0 (the "License"); you may not '''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
# use this file except in compliance with the License. You may obtain a copy '''Degrees'''.ko=온도
# of the License at: '''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
#
# 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.
# Korean (ko)
# Device Preferences
'''Yes'''.ko=
'''No'''.ko=아니요 '''No'''.ko=아니요
'''battery'''.ko=배터리 '''Tap to set'''.ko=눌러서 설정
'''Temperature Offset'''.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=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다. '''This feature allows you to correct any temperature variations by selecting an offset. Ex: If your sensor consistently reports a temp that's 5 degrees too warm, you'd enter '-5'. If 3 degrees too cold, enter '+3'.'''.ko=기준 온도를 원하는대로 몇 도 올리거나 내려서 설정할 수 있습니다.
'''Degrees'''.ko=온도
'''Adjust temperature by this many degrees'''.ko=몇 도씩 온도를 조절하십시오
'''Do you want to use this sensor on a garage door?'''.ko=차고 문의 센서 사용 설정하기
'''Tap to set'''.ko=눌러서 설정
'''Give your device a name'''.ko=기기 이름 설정
'''Multipurpose Sensor'''.ko=문 및 창 센서
# Events descriptionText
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}에서 열림이 감지되었습니다.
'''{{ device.displayName }} was closed'''.ko={{ device.displayName }}에서 닫힘이 감지되었습니다.
'''{{ device.displayName }} was active'''.ko={{ device.displayName }} 활성화
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }} 비활성화
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}에서 {{ value }}°C 감지
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다
'''{{ device.displayName }} battery was {{ value }}%'''.ko={{ device.displayName }}의 남은 배터리 {{ value }}%
'''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중 '''Updating device to garage sensor'''.ko=기기-차고 센서 업데이트 중
'''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중 '''Updating device to open/close sensor'''.ko=기기-열림/닫힘 센서 업데이트 중
'''Inactive'''.ko=비활성 상태 '''Yes'''.ko=
'''Active'''.ko=활성 상태 '''{{ device.displayName }} status was closed'''.ko={{ device.displayName }}은(는) 닫힌 상태입니다
'''Open'''.ko= 열림이 감지될 때 '''{{ device.displayName }} status was opened'''.ko={{ device.displayName }}은(는) 열린 상태입니다
'''Closed'''.ko=닫힘 '''{{ device.displayName }} was active'''.ko={{ device.displayName }}이(가) 활성화되었습니다
'''${currentValue}% battery'''.ko=${currentValue}% 배터리 '''{{ device.displayName }} was closed'''.ko={{ device.displayName }}이(가) 닫혔습니다
'''{{ device.displayName }} was inactive'''.ko={{ device.displayName }}이(가) 비활성화되었습니다
'''{{ device.displayName }} was opened'''.ko={{ device.displayName }}이(가) 열렸습니다
'''{{ device.displayName }} was {{ value }}°C'''.ko={{ device.displayName }}이(가) {{ value }}°C였습니다
'''{{ device.displayName }} was {{ value }}°F'''.ko={{ device.displayName }}이(가) {{ value }}°F였습니다

View File

@@ -1,19 +1,18 @@
/* /**
* Copyright 2016 SmartThings * SmartSense Multi
* *
* Licensed under the Apache License, Version 2.0 (the "License"); you may not * Copyright 2015 SmartThings
* 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 * 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
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * for the specific language governing permissions and limitations under the License.
* License for the specific language governing permissions and limitations *
* under the License.
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Multi Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -26,7 +25,6 @@ metadata {
capability "Acceleration Sensor" capability "Acceleration Sensor"
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Health Check"
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"
@@ -64,30 +62,30 @@ metadata {
]) ])
} }
section { 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 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?", 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){
tileAttribute ("device.status", key: "PRIMARY_CONTROL") { tileAttribute ("device.status", key: "PRIMARY_CONTROL") {
attributeState "open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e" attributeState "open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e"
attributeState "closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821" attributeState "closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821"
attributeState "garage-open", label:'Open', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e" attributeState "garage-open", label:'Open', icon:"st.doors.garage.garage-open", backgroundColor:"#ffa81e"
attributeState "garage-closed", label:'Closed', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821" attributeState "garage-closed", label:'Closed', icon:"st.doors.garage.garage-closed", backgroundColor:"#79b821"
} }
} }
standardTile("contact", "device.contact", width: 2, height: 2) { standardTile("contact", "device.contact", width: 2, height: 2) {
state("open", label:'Open', icon:"st.contact.contact.open", backgroundColor:"#ffa81e") state("open", label:'${name}', icon:"st.contact.contact.open", backgroundColor:"#ffa81e")
state("closed", label:'Closed', icon:"st.contact.contact.closed", backgroundColor:"#79b821") state("closed", label:'${name}', icon:"st.contact.contact.closed", backgroundColor:"#79b821")
} }
standardTile("acceleration", "device.acceleration", width: 2, height: 2) { standardTile("acceleration", "device.acceleration", width: 2, height: 2) {
state("active", label:'Active', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0") state("active", label:'${name}', icon:"st.motion.acceleration.active", backgroundColor:"#53a7c0")
state("inactive", label:'Inactive', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff") state("inactive", label:'${name}', icon:"st.motion.acceleration.inactive", backgroundColor:"#ffffff")
} }
valueTile("temperature", "device.temperature", width: 2, height: 2) { valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}°', state("temperature", label:'${currentValue}°',
@@ -147,33 +145,20 @@ private Map parseCatchAllMessage(String description) {
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
// 0x07 - configure reporting resultMap = getBatteryResult(cluster.data.last())
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break break
case 0xFC02: case 0xFC02:
log.debug 'ACCELERATION' log.debug 'ACCELERATION'
break break
case 0x0402: case 0x0402:
if (cluster.command == 0x07) { log.debug 'TEMP'
if(cluster.data[0] == 0x00) { // temp is last 2 data values. reverse to swap endian
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) def value = getTemperature(temp)
} resultMap = getTemperatureResult(value)
else { break
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
// 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
} }
} }
@@ -182,8 +167,10 @@ 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
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
@@ -236,13 +223,47 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2]
Map resultMap = [:] Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
if (garageSensor != "Yes"){ if (garageSensor != "Yes"){
resultMap = zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') resultMap = getContactResult('closed')
} }
break
case '0x0021': // Open/Motion/Wet
if (garageSensor != "Yes"){
resultMap = getContactResult('open')
}
break
case '0x0022': // Tamper Alarm
break
case '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
if (garageSensor != "Yes"){
resultMap = getContactResult('closed')
}
break
case '0x0025': // Restore Report
if (garageSensor != "Yes"){
resultMap = getContactResult('open')
}
break
case '0x0026': // Trouble/Failure
break
case '0x0028': // Test Mode
break
}
return resultMap return resultMap
} }
@@ -252,19 +273,19 @@ def updated() {
if (garageSensor == "Yes") { if (garageSensor == "Yes") {
def descriptionText = "Updating device to garage sensor" def descriptionText = "Updating device to garage sensor"
if (device.latestValue("status") == "open") { if (device.latestValue("status") == "open") {
sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText, translatable: true) sendEvent(name: 'status', value: 'garage-open', descriptionText: descriptionText)
} }
else if (device.latestValue("status") == "closed") { else if (device.latestValue("status") == "closed") {
sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText, translatable: true) sendEvent(name: 'status', value: 'garage-closed', descriptionText: descriptionText)
} }
} }
else { else {
def descriptionText = "Updating device to open/close sensor" def descriptionText = "Updating device to open/close sensor"
if (device.latestValue("status") == "garage-open") { if (device.latestValue("status") == "garage-open") {
sendEvent(name: 'status', value: 'open', descriptionText: descriptionText, translatable: true) sendEvent(name: 'status', value: 'open', descriptionText: descriptionText)
} }
else if (device.latestValue("status") == "garage-closed") { else if (device.latestValue("status") == "garage-closed") {
sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText, translatable: true) sendEvent(name: 'status', value: 'closed', descriptionText: descriptionText)
} }
} }
} }
@@ -272,19 +293,19 @@ def updated() {
def getTemperature(value) { def getTemperature(value) {
def celsius = Integer.parseInt(value, 16).shortValue() / 100 def celsius = Integer.parseInt(value, 16).shortValue() / 100
if(getTemperatureScale() == "C"){ if(getTemperatureScale() == "C"){
return Math.round(celsius) return celsius
} else { } else {
return Math.round(celsiusToFahrenheit(celsius)) return celsiusToFahrenheit(celsius) as Integer
} }
} }
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',
value: '--', value: '--'
translatable: true
] ]
def volts = rawValue / 10 def volts = rawValue / 10
@@ -292,7 +313,7 @@ private Map getBatteryResult(rawValue) {
if (rawValue == 0 || rawValue == 255) {} if (rawValue == 0 || rawValue == 255) {}
else { else {
if (volts > 3.5) { if (volts > 3.5) {
result.descriptionText = "{{ device.displayName }} battery has too much power: (> 3.5) volts." result.descriptionText = "${linkText} battery has too much power (${volts} volts)."
} }
else { else {
if (device.getDataValue("manufacturer") == "SmartThings") { if (device.getDataValue("manufacturer") == "SmartThings") {
@@ -309,18 +330,15 @@ 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 }}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
} }
else { else {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0) result.descriptionText = "${linkText} battery was ${result.value}%"
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "{{ device.displayName }} battery was {{ value }}%"
} }
} }
} }
@@ -330,61 +348,43 @@ private Map getBatteryResult(rawValue) {
private Map getTemperatureResult(value) { private Map getTemperatureResult(value) {
log.debug "Temperature" log.debug "Temperature"
def linkText = getLinkText(device)
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': def descriptionText = "${linkText} was ${value}°${temperatureScale}"
'{{ device.displayName }} was {{ value }}°F'
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
translatable: true,
unit: temperatureScale
] ]
} }
private Map getContactResult(value) { private Map getContactResult(value) {
log.debug "Contact: ${device.displayName} value = ${value}" log.debug "Contact"
def descriptionText = value == 'open' ? '{{ device.displayName }} was opened' : '{{ device.displayName }} was closed' def linkText = getLinkText(device)
sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed: false, translatable: true) def descriptionText = "${linkText} was ${value == 'open' ? 'opened' : 'closed'}"
sendEvent(name: 'status', value: value, descriptionText: descriptionText, translatable: true) sendEvent(name: 'contact', value: value, descriptionText: descriptionText, displayed:false)
sendEvent(name: 'status', value: value, descriptionText: descriptionText)
} }
private getAccelerationResult(numValue) { private getAccelerationResult(numValue) {
log.debug "Acceleration" log.debug "Acceleration"
def name = "acceleration" def name = "acceleration"
def value def value = numValue.endsWith("1") ? "active" : "inactive"
def descriptionText def linkText = getLinkText(device)
def descriptionText = "$linkText was $value"
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) def isStateChange = isStateChange(device, name, value)
return [ [
name: name, name: name,
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText,
isStateChange: isStateChange, isStateChange: isStateChange
translatable: true
] ]
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() { def refresh() {
log.debug "Refreshing Values " log.debug "Refreshing Values "
@@ -392,42 +392,78 @@ def refresh() {
if (device.getDataValue("manufacturer") == "SmartThings") { if (device.getDataValue("manufacturer") == "SmartThings") {
log.debug "Refreshing Values for manufacturer: SmartThings " log.debug "Refreshing Values for manufacturer: SmartThings "
/* These values of Motion Threshold Multiplier(0x01) and Motion Threshold (0x0276) refreshCmds = refreshCmds + [
seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer. /* These values of Motion Threshold Multiplier(01) and Motion Threshold (7602)
Separating these out in a separate if-else because I do not want to touch Centralite part seem to be giving pretty accurate results for the XYZ co-ordinates for this manufacturer.
as of now. Separating these out in a separate if-else because I do not want to touch Centralite part
*/ as of now.
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x01, [mfgCode: manufacturerCode]) */
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0002, 0x21, 0x0276, [mfgCode: manufacturerCode])
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global write 0xFC02 0 0x20 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global write 0xFC02 2 0x21 {7602}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
]
} else { } else {
refreshCmds += zigbee.writeAttribute(0xFC02, 0x0000, 0x20, 0x02, [mfgCode: manufacturerCode]) refreshCmds = refreshCmds + [
/* sensitivity - default value (8) */
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global write 0xFC02 0 0x20 {02}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 400",
]
} }
//Common refresh commands //Common refresh commands
refreshCmds += zigbee.readAttribute(0x0402, 0x0000) + refreshCmds = refreshCmds + [
zigbee.readAttribute(0x0001, 0x0020) + "st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
zigbee.readAttribute(0xFC02, 0x0010, [mfgCode: manufacturerCode]) "st rattr 0x${device.deviceNetworkId} 1 1 0x20", "delay 200",
"zcl mfg-code ${manufacturerCode}", "delay 200",
"zcl global read 0xFC02 0x0010",
"send 0x${device.deviceNetworkId} 1 1","delay 400"
]
return refreshCmds + enrollResponse() return refreshCmds + enrollResponse()
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time) String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
log.debug "Configuring Reporting" log.debug "Configuring Reporting"
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity def configCmds = [
// battery minReport 30 seconds, maxReportTime 6 hrs by default "zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
def configCmds = zigbee.batteryConfig() + "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500",
zigbee.temperatureConfig(30, 300) +
zigbee.configureReporting(0xFC02, 0x0010, 0x18, 10, 3600, 0x01, [mfgCode: manufacturerCode]) +
zigbee.configureReporting(0xFC02, 0x0012, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
zigbee.configureReporting(0xFC02, 0x0013, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode]) +
zigbee.configureReporting(0xFC02, 0x0014, 0x29, 1, 3600, 0x0001, [mfgCode: manufacturerCode])
return refresh() + configCmds "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
"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",
"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",
"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",
"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",
"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",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
]
return configCmds + refresh()
} }
private getEndpointId() { private getEndpointId() {
@@ -492,9 +528,10 @@ 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) def descriptionText = "${linkText} was ${contactValue == 'open' ? 'opened' : 'closed'}"
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText, translatable: true) sendEvent(name: 'contact', value: contactValue, descriptionText: descriptionText, displayed:false)
sendEvent(name: 'status', value: garageValue, descriptionText: descriptionText)
} }
} }

View File

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

View File

@@ -14,20 +14,17 @@
* *
*/ */
//DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH //DEPRECATED - Using the smartsense-multi-sensor.groovy DTH for this device. Users need to be moved before deleting this DTH
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings", category: "C2") { definition (name: "SmartSense Open/Closed Accelerometer Sensor", namespace: "smartthings", author: "SmartThings") {
capability "Battery" capability "Battery"
capability "Configuration" capability "Configuration"
capability "Contact Sensor" capability "Contact Sensor"
capability "Acceleration Sensor" capability "Acceleration Sensor"
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Health Check" command "enrollResponse"
capability "Sensor"
command "enrollResponse"
} }
simulator { simulator {
@@ -173,9 +170,40 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2]
return zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getContactResult('closed')
break
case '0x0021': // Open/Motion/Wet
resultMap = getContactResult('open')
break
case '0x0022': // Tamper Alarm
break
case '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
resultMap = getContactResult('closed')
break
case '0x0025': // Restore Report
resultMap = getContactResult('open')
break
case '0x0026': // Trouble/Failure
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -205,10 +233,7 @@ def getTemperature(value) {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "${linkText} battery was ${result.value}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
@@ -225,10 +250,9 @@ def getTemperature(value) {
} }
def descriptionText = "${linkText} was ${value}°${temperatureScale}" def descriptionText = "${linkText} was ${value}°${temperatureScale}"
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }
@@ -275,10 +299,13 @@ def getTemperature(value) {
} }
def configure() { def configure() {
sendEvent(name: "checkInterval", value: 7200, displayed: false)
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",
"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}", //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",
@@ -291,7 +318,7 @@ def configure() {
"zcl global send-me-a-report 0xFC02 2 0x18 30 3600 {01}", "zcl global send-me-a-report 0xFC02 2 0x18 30 3600 {01}",
"send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500" "send 0x${device.deviceNetworkId} 1 ${endpointId}", "delay 500"
] ]
return refresh() + configCmds // send refresh cmds as part of config return configCmds + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,41 +0,0 @@
# Smartsense Open/Closed Sensor
Works with:
* [Samsung SmartThings Open/Closed Sensor](https://shop.smartthings.com/#!/packs/smartsense-open-closed-sensor/)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Battery** - defines device uses a battery
* **Contact Sensor** - can detect contact (possible values: open,closed)
* **Refresh** - _refresh()_ command for status updates
* **Temperature Measurement** - defines device measures current temperature
* **Health Check** - indicates ability to get device health notifications
* **Sensor** - detects sensor events
## Device Health
A Category C2 open/closed sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Battery Specification
One CR2 3V battery required.
## Troubleshooting
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
* [SmartSense Open/Closed Sensor Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/202836844-SmartSense-Open-Closed-Sensor)

View File

@@ -13,7 +13,6 @@
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "SmartSense Open/Closed Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -22,8 +21,6 @@ metadata {
capability "Contact Sensor" capability "Contact Sensor"
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Health Check"
capability "Sensor"
command "enrollResponse" command "enrollResponse"
@@ -109,28 +106,15 @@ private Map parseCatchAllMessage(String description) {
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
// 0x07 - configure reporting resultMap = getBatteryResult(cluster.data.last())
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break break
case 0x0402: case 0x0402:
if (cluster.command == 0x07){ log.debug 'TEMP'
if (cluster.data[0] == 0x00) { // temp is last 2 data values. reverse to swap endian
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) def value = getTemperature(temp)
} resultMap = getTemperatureResult(value)
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
// 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 break
} }
} }
@@ -140,8 +124,10 @@ 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
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
@@ -179,8 +165,40 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
return zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') String msgCode = parsedMsg[2]
Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getContactResult('closed')
break
case '0x0021': // Open/Motion/Wet
resultMap = getContactResult('open')
break
case '0x0022': // Tamper Alarm
break
case '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
resultMap = getContactResult('closed')
break
case '0x0025': // Restore Report
resultMap = getContactResult('open')
break
case '0x0026': // Trouble/Failure
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -210,10 +228,7 @@ private Map getBatteryResult(rawValue) {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "${linkText} battery was ${result.value}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
@@ -232,8 +247,7 @@ private Map getTemperatureResult(value) {
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }
@@ -248,13 +262,6 @@ private Map getContactResult(value) {
] ]
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
}
def refresh() { def refresh() {
log.debug "Refreshing Temperature and Battery" log.debug "Refreshing Temperature and Battery"
def refreshCmds = [ def refreshCmds = [
@@ -266,15 +273,22 @@ def refresh() {
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
String zigbeeEui = swapEndianHex(device.hub.zigbeeEui)
log.debug "Configuring Reporting, IAS CIE, and Bindings." log.debug "Configuring Reporting, IAS CIE, and Bindings."
def configCmds = [
"zcl global write 0x500 0x10 0xf0 {${zigbeeEui}}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 500",
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity "zdo bind 0x${device.deviceNetworkId} ${endpointId} 1 1 {${device.zigbeeId}} {}", "delay 500",
// battery minReport 30 seconds, maxReportTime 6 hrs by default "zcl global send-me-a-report 1 0x20 0x20 30 21600 {01}", //checkin time 6 hrs
return refresh() + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config "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"
]
return configCmds + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,41 +0,0 @@
# SmartSense Temp/Humidity Sensor
Works with:
* [Samsung SmartSense Temp/Humidity Sensor](https://shop.smartthings.com/#!/products/smartsense-temp-humidity-sensor)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Battery** - defines device uses a battery
* **Relative Humidity Measurement** - defines device measures relative humidity
* **Refresh** - _refresh()_ command for status updates
* **Temperature Measurement** - defines device measures current temperature
* **Health Check** - indicates ability to get device health notifications
* **Sensor** - detects sensor events
## Device Health
A Category C2 SmartSense Temp/Humidity Sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 mins
## Battery Specification
One CR2 battery is required.
## Troubleshooting
If the sensor doesn't pair when trying from the SmartThings mobile app, it is possible that the sensor is out of range.
Pairing needs to be tried by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the sensor from SmartThings can be found in the following link:
* [Troubleshooting Tips](https://support.smartthings.com/hc/en-us/articles/203040294)

View File

@@ -20,8 +20,6 @@ metadata {
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Health Check"
capability "Sensor"
fingerprint endpointId: "01", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003" fingerprint endpointId: "01", inClusters: "0001,0003,0020,0402,0B05,FC45", outClusters: "0019,0003"
} }
@@ -93,37 +91,20 @@ private Map parseCatchAllMessage(String description) {
if (shouldProcessMessage(cluster)) { if (shouldProcessMessage(cluster)) {
switch(cluster.clusterId) { switch(cluster.clusterId) {
case 0x0001: case 0x0001:
// 0x07 - configure reporting resultMap = getBatteryResult(cluster.data.last())
if (cluster.command != 0x07) {
resultMap = getBatteryResult(cluster.data.last())
}
break break
case 0x0402: case 0x0402:
if (cluster.command == 0x07) { // temp is last 2 data values. reverse to swap endian
if (cluster.data[0] == 0x00){ String temp = cluster.data[-2..-1].reverse().collect { cluster.hex1(it) }.join()
log.debug "TEMP REPORTING CONFIG RESPONSE" + cluster def value = getTemperature(temp)
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID]) resultMap = getTemperatureResult(value)
} break
else {
log.warn "TEMP REPORTING CONFIG FAILED- error code:${cluster.data[0]}"
}
}
else {
// 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 0xFC45: case 0xFC45:
// 0x07 - configure reporting String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('')
if (cluster.command != 0x07) { String display = Math.round(Integer.valueOf(pctStr, 16) / 100)
String pctStr = cluster.data[-1, -2].collect { Integer.toHexString(it) }.join('') resultMap = getHumidityResult(display)
String display = Math.round(Integer.valueOf(pctStr, 16) / 100)
resultMap = getHumidityResult(display)
}
break break
} }
} }
@@ -133,8 +114,10 @@ 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
boolean ignoredMessage = cluster.profileId != 0x0104 || boolean ignoredMessage = cluster.profileId != 0x0104 ||
cluster.command == 0x0B || cluster.command == 0x0B ||
cluster.command == 0x07 ||
(cluster.data.size() > 0 && cluster.data.first() == 0x3e) (cluster.data.size() > 0 && cluster.data.first() == 0x3e)
return !ignoredMessage return !ignoredMessage
} }
@@ -221,10 +204,7 @@ private Map getBatteryResult(rawValue) {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
if (roundedPct <= 0)
roundedPct = 1
result.value = Math.min(100, roundedPct)
result.descriptionText = "${linkText} battery was ${result.value}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
@@ -243,47 +223,50 @@ private Map getTemperatureResult(value) {
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }
private Map getHumidityResult(value) { private Map getHumidityResult(value) {
log.debug 'Humidity' log.debug 'Humidity'
return value ? [name: 'humidity', value: value, unit: '%'] : [:] return [
} name: 'humidity',
value: value,
/** unit: '%'
* PING is used by Device-Watch in attempt to reach the Device ]
* */
def ping() {
return zigbee.readAttribute(0x001, 0x0020) // Read the Battery Level
} }
def refresh() def refresh()
{ {
log.debug "refresh temperature, humidity, and battery" log.debug "refresh temperature, humidity, and battery"
return zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0xC2DF]) + // Original firmware [
zigbee.readAttribute(0xFC45, 0x0000, ["mfgCode": 0x104E]) + // New firmware
zigbee.readAttribute(0x0402, 0x0000) + "zcl mfg-code 0xC2DF", "delay 1000",
zigbee.readAttribute(0x0001, 0x0020) "zcl global read 0xFC45 0", "delay 1000",
"send 0x${device.deviceNetworkId} 1 1", "delay 1000",
"st rattr 0x${device.deviceNetworkId} 1 0x402 0", "delay 200",
"st rattr 0x${device.deviceNetworkId} 1 1 0x20"
]
} }
def configure() { def configure() {
// Device-Watch allows 3 check-in misses from device (plus 1 min lag time)
// enrolls with default periodic reporting until newer 5 min interval is confirmed
sendEvent(name: "checkInterval", value: 3 * 60 * 60 + 1 * 60, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
log.debug "Configuring Reporting and Bindings." log.debug "Configuring Reporting and Bindings."
def humidityConfigCmds = [ def configCmds = [
"zdo bind 0x${device.deviceNetworkId} 1 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",
"zdo bind 0x${device.deviceNetworkId} 1 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",
"zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500", "zdo bind 0x${device.deviceNetworkId} 1 1 0xFC45 {${device.zigbeeId}} {}", "delay 500",
"zcl global send-me-a-report 0xFC45 0 0x29 30 3600 {6400}", "zcl global send-me-a-report 0xFC45 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
// temperature minReportTime 30 seconds, maxReportTime 5 min. Reporting interval if no activity
// battery minReport 30 seconds, maxReportTime 6 hrs by default
return refresh() + humidityConfigCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) // send refresh cmds as part of config
} }
private hex(value) { private hex(value) {

View File

@@ -16,8 +16,6 @@
metadata { metadata {
definition (name: "Simulated Alarm", namespace: "smartthings/testing", author: "SmartThings") { definition (name: "Simulated Alarm", namespace: "smartthings/testing", author: "SmartThings") {
capability "Alarm" capability "Alarm"
capability "Sensor"
capability "Actuator"
} }
simulator { simulator {

View File

@@ -1,8 +1,6 @@
metadata { metadata {
definition (name: "Simulated Color Control", namespace: "smartthings/testing", author: "SmartThings") { definition (name: "Simulated Color Control", namespace: "smartthings/testing", author: "SmartThings") {
capability "Color Control" capability "Color Control"
capability "Sensor"
capability "Actuator"
} }
simulator { simulator {

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Simulated Contact Sensor", namespace: "smartthings/testing", author: "bob") { definition (name: "Simulated Contact Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Contact Sensor" capability "Contact Sensor"
capability "Sensor"
command "open" command "open"
command "close" command "close"

View File

@@ -15,8 +15,6 @@ metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Simulated Lock", namespace: "smartthings/testing", author: "bob") { definition (name: "Simulated Lock", namespace: "smartthings/testing", author: "bob") {
capability "Lock" capability "Lock"
capability "Sensor"
capability "Actuator"
} }
// Simulated lock // Simulated lock

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Simulated Motion Sensor", namespace: "smartthings/testing", author: "bob") { definition (name: "Simulated Motion Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Motion Sensor" capability "Motion Sensor"
capability "Sensor"
command "active" command "active"
command "inactive" command "inactive"

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Simulated Presence Sensor", namespace: "smartthings/testing", author: "bob") { definition (name: "Simulated Presence Sensor", namespace: "smartthings/testing", author: "bob") {
capability "Presence Sensor" capability "Presence Sensor"
capability "Sensor"
command "arrived" command "arrived"
command "departed" command "departed"

View File

@@ -16,8 +16,6 @@ metadata {
definition (name: "Simulated Switch", namespace: "smartthings/testing", author: "bob") { definition (name: "Simulated Switch", namespace: "smartthings/testing", author: "bob") {
capability "Switch" capability "Switch"
capability "Relay Switch" capability "Relay Switch"
capability "Sensor"
capability "Actuator"
command "onPhysical" command "onPhysical"
command "offPhysical" command "offPhysical"

View File

@@ -16,7 +16,6 @@ metadata {
definition (name: "Simulated Temperature Sensor", namespace: "smartthings/testing", author: "SmartThings") { definition (name: "Simulated Temperature Sensor", namespace: "smartthings/testing", author: "SmartThings") {
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Switch Level" capability "Switch Level"
capability "Sensor"
command "up" command "up"
command "down" command "down"

View File

@@ -16,8 +16,6 @@ metadata {
definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") { definition (name: "Simulated Thermostat", namespace: "smartthings/testing", author: "SmartThings") {
capability "Thermostat" capability "Thermostat"
capability "Relative Humidity Measurement" capability "Relative Humidity Measurement"
capability "Sensor"
capability "Actuator"
command "tempUp" command "tempUp"
command "tempDown" command "tempDown"

View File

@@ -15,7 +15,6 @@ metadata {
// Automatically generated. Make future change here. // Automatically generated. Make future change here.
definition (name: "Simulated Water Sensor", namespace: "smartthings/testing", author: "SmartThings") { definition (name: "Simulated Water Sensor", namespace: "smartthings/testing", author: "SmartThings") {
capability "Water Sensor" capability "Water Sensor"
capability "Sensor"
command "wet" command "wet"
command "dry" command "dry"

View File

@@ -1,42 +0,0 @@
# Device Tiles Examples and Reference
This package contains examples of Device tiles, organized by tile type.
## Purpose
Each Device Handler shows example usages of a specific tile, and is meant to represent the variety of permutations that a tile can be configured.
The various tiles can be used by QA to test tiles on all supported mobile devices, and by developers as a reference implementation.
## Installation
1. Self-publish the Device Handlers in this package.
2. Self-publish the Device Tile Controller SmartApp. The SmartApp can be found [here](https://github.com/SmartThingsCommunity/SmartThingsPublic/blob/master/smartapps/smartthings/tile-ux/device-tile-controller.src/device-tile-controller.groovy).
3. Install the SmartApp from the Marketplace, under "My Apps".
4. Select the simulated devices you want to install and press "Done".
The simulated devices can then be found in the "Things" view of "My Home" in the mobile app.
You may wish to create a new room for these simulated devices for easy access.
## Usage
Each simulated device can be interacted with like other devices.
You can use the mobile app to interact with the tiles to see how they look and behave.
## Troubleshooting
If you get an error when installing the simulated devices using the controller SmartApp, ensure that you have published all the Device Handlers for yourself.
Also check live logging to see if there is a specific tile that is causing installation issues.
## FAQ
*Question: A tile isn't behaving as expected. What should I do?*
QA should create a JIRA ticket for any issues or inconsistencies of tiles across devices.
Developers may file a support ticket, and reference the specific tile and issue observed.
*Question: I'd like to contribute an example tile usage that would be helpful for testing and reference purposes. Can I do that?*
We recommend that you open an issue in the SmartThingsPublic repository describing the example tile and usage.
That way we can discuss with you the proposed change, and then if appropriate you can create a PR associated to the issue.

View File

@@ -1,225 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "carouselDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Thermostat"
capability "Relative Humidity Measurement"
command "tempUp"
command "tempDown"
command "heatUp"
command "heatDown"
command "coolUp"
command "coolDown"
command "setTemperature", ["number"]
}
tiles(scale: 2) {
multiAttributeTile(name:"thermostatMulti", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("default", label:'${currentValue}', unit:"dF")
}
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("default", action: "setTemperature")
}
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
attributeState("default", label:'${currentValue}%', unit:"%")
}
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
attributeState("idle", backgroundColor:"#44b621")
attributeState("heating", backgroundColor:"#ffa81e")
attributeState("cooling", backgroundColor:"#269bd2")
}
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
attributeState("off", label:'${name}')
attributeState("heat", label:'${name}')
attributeState("cool", label:'${name}')
attributeState("auto", label:'${name}')
}
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF")
}
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("default", label:'${currentValue}', unit:"dF")
}
}
main("thermostatMulti")
details([
"thermostatMulti"
])
}
}
def installed() {
sendEvent(name: "temperature", value: 72, unit: "F")
sendEvent(name: "heatingSetpoint", value: 70, unit: "F")
sendEvent(name: "thermostatSetpoint", value: 70, unit: "F")
sendEvent(name: "coolingSetpoint", value: 76, unit: "F")
sendEvent(name: "thermostatMode", value: "off")
sendEvent(name: "thermostatFanMode", value: "fanAuto")
sendEvent(name: "thermostatOperatingState", value: "idle")
sendEvent(name: "humidity", value: 53, unit: "%")
}
def parse(String description) {
}
def evaluate(temp, heatingSetpoint, coolingSetpoint) {
log.debug "evaluate($temp, $heatingSetpoint, $coolingSetpoint"
def threshold = 1.0
def current = device.currentValue("thermostatOperatingState")
def mode = device.currentValue("thermostatMode")
def heating = false
def cooling = false
def idle = false
if (mode in ["heat","emergency heat","auto"]) {
if (heatingSetpoint - temp >= threshold) {
heating = true
sendEvent(name: "thermostatOperatingState", value: "heating")
}
else if (temp - heatingSetpoint >= threshold) {
idle = true
}
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
}
if (mode in ["cool","auto"]) {
if (temp - coolingSetpoint >= threshold) {
cooling = true
sendEvent(name: "thermostatOperatingState", value: "cooling")
}
else if (coolingSetpoint - temp >= threshold && !heating) {
idle = true
}
sendEvent(name: "thermostatSetpoint", value: coolingSetpoint)
}
else {
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
}
if (idle && !heating && !cooling) {
sendEvent(name: "thermostatOperatingState", value: "idle")
}
}
def setHeatingSetpoint(Double degreesF) {
log.debug "setHeatingSetpoint($degreesF)"
sendEvent(name: "heatingSetpoint", value: degreesF)
evaluate(device.currentValue("temperature"), degreesF, device.currentValue("coolingSetpoint"))
}
def setCoolingSetpoint(Double degreesF) {
log.debug "setCoolingSetpoint($degreesF)"
sendEvent(name: "coolingSetpoint", value: degreesF)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), degreesF)
}
def setThermostatMode(String value) {
sendEvent(name: "thermostatMode", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def setThermostatFanMode(String value) {
sendEvent(name: "thermostatFanMode", value: value)
}
def off() {
sendEvent(name: "thermostatMode", value: "off")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def heat() {
sendEvent(name: "thermostatMode", value: "heat")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def auto() {
sendEvent(name: "thermostatMode", value: "auto")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def emergencyHeat() {
sendEvent(name: "thermostatMode", value: "emergency heat")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def cool() {
sendEvent(name: "thermostatMode", value: "cool")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def fanOn() {
sendEvent(name: "thermostatFanMode", value: "fanOn")
}
def fanAuto() {
sendEvent(name: "thermostatFanMode", value: "fanAuto")
}
def fanCirculate() {
sendEvent(name: "thermostatFanMode", value: "fanCirculate")
}
def tempUp() {
def ts = device.currentState("temperature")
def value = ts ? ts.integerValue + 1 : 72
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def tempDown() {
def ts = device.currentState("temperature")
def value = ts ? ts.integerValue - 1 : 72
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def setTemperature(value) {
def ts = device.currentState("temperature")
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def heatUp() {
def ts = device.currentState("heatingSetpoint")
def value = ts ? ts.integerValue + 1 : 68
sendEvent(name:"heatingSetpoint", value: value)
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
}
def heatDown() {
def ts = device.currentState("heatingSetpoint")
def value = ts ? ts.integerValue - 1 : 68
sendEvent(name:"heatingSetpoint", value: value)
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
}
def coolUp() {
def ts = device.currentState("coolingSetpoint")
def value = ts ? ts.integerValue + 1 : 76
sendEvent(name:"coolingSetpoint", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
}
def coolDown() {
def ts = device.currentState("coolingSetpoint")
def value = ts ? ts.integerValue - 1 : 76
sendEvent(name:"coolingSetpoint", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
}

View File

@@ -1,59 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "colorWheelDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Color Control"
}
tiles(scale: 2) {
valueTile("currentColor", "device.color") {
state "color", label: '${currentValue}', defaultState: true
}
controlTile("rgbSelector", "device.color", "color", height: 6, width: 6, inactiveLabel: false) {
state "color", action: "color control.setColor"
}
main("currentColor")
details([
"rgbSelector"
])
}
}
// parse events into attributes
def parse(String description) {
log.debug "Parsing '${description}'"
}
def setColor(value) {
log.debug "setting color: $value"
if (value.hex) { sendEvent(name: "color", value: value.hex) }
if (value.hue) { sendEvent(name: "hue", value: value.hue) }
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation) }
}
def setSaturation(percent) {
log.debug "Executing 'setSaturation'"
sendEvent(name: "saturation", value: percent)
}
def setHue(percent) {
log.debug "Executing 'setHue'"
sendEvent(name: "hue", value: percent)
}

View File

@@ -1,63 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "presenceDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Presence Sensor"
command "arrived"
command "departed"
}
tiles(scale: 2) {
// You only get a presence tile view when the size is 3x3 otherwise it's a value tile
standardTile("presence", "device.presence", width: 3, height: 3, canChangeBackground: true) {
state("present", labelIcon:"st.presence.tile.mobile-present", backgroundColor:"#53a7c0")
state("not present", labelIcon:"st.presence.tile.mobile-not-present", backgroundColor:"#ebeef2")
}
standardTile("notPresentBtn", "device.fake", width: 3, height: 3, decoration: "flat") {
state("not present", label:'not present', backgroundColor:"#ffffff", action:"departed")
}
standardTile("presentBtn", "device.fake", width: 3, height: 3, decoration: "flat") {
state("present", label:'present', backgroundColor:"#53a7c0", action:"arrived")
}
main("presence")
details([
"presence", "presenceControl", "notPresentBtn", "presentBtn"
])
}
}
def installed() {
sendEvent(name: "presence", value: "present")
}
def parse(String description) {
}
def arrived() {
log.trace "Executing 'arrived'"
sendEvent(name: "presence", value: "present")
}
def departed() {
log.trace "Executing 'arrived'"
sendEvent(name: "presence", value: "not present")
}

View File

@@ -1,75 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "sliderDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Switch Level"
command "setRangedLevel", ["number"]
}
tiles(scale: 2) {
controlTile("tinySlider", "device.level", "slider", height: 2, width: 2, inactiveLabel: false) {
state "level", action:"switch level.setLevel"
}
controlTile("mediumSlider", "device.level", "slider", height: 2, width: 4, inactiveLabel: false) {
state "level", action:"switch level.setLevel"
}
controlTile("largeSlider", "device.level", "slider", decoration: "flat", height: 2, width: 6, inactiveLabel: false) {
state "level", action:"switch level.setLevel"
}
controlTile("rangeSlider", "device.rangedLevel", "slider", height: 2, width: 4, range: "(20..80)") {
state "level", action:"setRangedLevel"
}
valueTile("rangeValue", "device.rangedLevel", height: 2, width: 2) {
state "range", label:'${currentValue}', defaultState: true
}
controlTile("rangeSliderConstrained", "device.rangedLevel", "slider", height: 2, width: 4, range: "(40..60)") {
state "level", action:"setRangedLevel"
}
main("rangeValue")
details([
"tinySlider", "mediumSlider",
"largeSlider",
"rangeSlider", "rangeValue",
"rangeSliderConstrained"
])
}
}
def installed() {
sendEvent(name: "level", value: 63)
sendEvent(name: "rangedLevel", value: 47)
}
def parse(String description) {
}
def setLevel(value) {
log.debug "setting level to $value"
sendEvent(name:"level", value:value)
}
def setRangedLevel(value) {
log.debug "setting ranged level to $value"
sendEvent(name:"rangedLevel", value:value)
}

View File

@@ -1,121 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "standardDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Switch"
}
tiles(scale: 2) {
// standard tile with actions
standardTile("actionRings", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: '${currentValue}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: '${currentValue}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
}
// standard flat tile with actions
standardTile("actionFlat", "device.switch", width: 2, height: 2, canChangeIcon: true, decoration: "flat") {
state "off", label: '${currentValue}', action: "switch.on", icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: '${currentValue}', action: "switch.off", icon: "st.switches.switch.on", backgroundColor: "#79b821"
}
// standard flat tile without actions
standardTile("noActionFlat", "device.switch", width: 2, height: 2, canChangeIcon: true) {
state "off", label: '${currentValue}',icon: "st.switches.switch.off", backgroundColor: "#ffffff"
state "on", label: '${currentValue}', icon: "st.switches.switch.on", backgroundColor: "#79b821"
}
// standard flat tile with only a label
standardTile("flatLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
state "label", label: 'On Action', action: "switch.on", backgroundColor: "#ffffff", defaultState: true
}
// standard flat tile with icon and label
standardTile("flatIconLabel", "device.switch", width: 2, height: 2, decoration: "flat") {
state "iconLabel", label: 'Off Action', action: "switch.off", icon:"st.switches.switch.off", backgroundColor: "#ffffff", defaultState: true
}
// standard flat tile with only icon (Refreh text is IN the icon file)
standardTile("flatIcon", "device.switch", width: 2, height: 2, decoration: "flat") {
state "icon", action:"refresh.refresh", icon:"st.secondary.refresh", defaultState: true
}
// standard with defaultState = true
standardTile("flatDefaultState", "null", width: 2, height: 2, decoration: "flat") {
state "off", label: 'Fail!', icon: "st.switches.switch.off"
state "on", label: 'Pass!', icon: "st.switches.switch.on", defaultState: true
}
// standard with implicit defaultState based on order (0 index is selected)
standardTile("flatImplicitDefaultState1", "null", width: 2, height: 2, decoration: "flat") {
state "on", label: 'Pass!', icon: "st.switches.switch.on"
state "off", label: 'Fail!', icon: "st.switches.switch.off"
}
// standard with implicit defaultState based on state.name == default
standardTile("flatImplicitDefaultState2", "null", width: 2, height: 2, decoration: "flat") {
state "off", label: 'Fail!', icon: "st.switches.switch.off"
state "default", label: 'Pass!', icon: "st.switches.switch.on"
}
// utility tiles to fill the spaces
standardTile("empty2x2", "null", width: 2, height: 2, decoration: "flat") {
state "emptySmall", label:'', defaultState: true
}
standardTile("empty4x2", "null", width: 4, height: 2, decoration: "flat") {
state "emptyBigger", label:'', defaultState: true
}
// multi-line text (explicit newlines)
standardTile("multiLine", "device.multiLine", width: 2, height: 2) {
state "multiLine", label: '${currentValue}', defaultState: true
}
standardTile("multiLineWithIcon", "device.multiLine", width: 2, height: 2) {
state "multiLineIcon", label: '${currentValue}', icon: "st.switches.switch.off", defaultState: true
}
main("actionRings")
details([
"actionRings", "actionFlat", "noActionFlat",
"flatLabel", "flatIconLabel", "flatIcon",
"flatDefaultState", "flatImplicitDefaultState1", "flatImplicitDefaultState2",
"multiLine", "multiLineWithIcon"
])
}
}
def installed() {
sendEvent(name: "switch", value: "off")
sendEvent(name: "multiLine", value: "Line 1\nLine 2\nLine 3")
}
def parse(String description) {
}
def on() {
log.debug "on()"
sendEvent(name: "switch", value: "on")
}
def off() {
log.debug "off()"
sendEvent(name: "switch", value: "off")
}

View File

@@ -1,106 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "valueDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Sensor"
}
tiles(scale: 2) {
valueTile("text", "device.text", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("longText", "device.longText", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("integer", "device.integer", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("integerFloat", "device.integerFloat", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("pi", "device.pi", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("floatAsText", "device.floatAsText", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true
}
valueTile("bgColor", "device.integer", width: 2, height: 2) {
state "val", label:'${currentValue}', backgroundColor: "#e86d13", defaultState: true
}
valueTile("bgColorRange", "device.integer", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#ff0000"],
[value: 90, color: "#0000ff"]
]
}
valueTile("bgColorRangeSingleItem", "device.integer", width: 2, height: 2) {
state "val", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#333333"]
]
}
valueTile("bgColorRangeConflict", "device.integer", width: 2, height: 2) {
state "valWithConflict", label:'${currentValue}', defaultState: true, backgroundColors: [
[value: 10, color: "#990000"],
[value: 10, color: "#000099"]
]
}
valueTile("noValue", "device.nada", width: 4, height: 2) {
state "noval", label:'${currentValue}', defaultState: true
}
valueTile("multiLine", "device.multiLine", width: 3, height: 2) {
state "val", label: '${currentValue}', defaultState: true
}
valueTile("multiLineWithIcon", "device.multiLine", width: 3, height: 2) {
state "val", label: '${currentValue}', icon: "st.switches.switch.off", defaultState: true
}
main("text")
details([
"text", "longText", "integer",
"integerFloat", "pi", "floatAsText",
"bgColor", "bgColorRange", "bgColorRangeSingleItem",
"bgColorRangeConflict", "noValue",
"multiLine", "multiLineWithIcon"
])
}
}
def installed() {
sendEvent(name: "text", value: "Test")
sendEvent(name: "longText", value: "The Longer The Text, The Better The Test")
sendEvent(name: "integer", value: 47)
sendEvent(name: "integerFloat", value: 47.0)
sendEvent(name: "pi", value: 3.14159)
sendEvent(name: "floatAsText", value: "3.14159")
sendEvent(name: "multiLine", value: "Line 1\nLine 2\nLine 3")
}
def parse(String description) {
}

View File

@@ -1,151 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "genericDeviceTile", namespace: "smartthings/tile-ux", author: "SmartThings") {
capability "Actuator"
capability "Switch"
capability "Switch Level"
command "levelUp"
command "levelDown"
command "randomizeLevel"
}
tiles(scale: 2) {
multiAttributeTile(name:"basicTile", type:"generic", width:6, height:4) {
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', action:"switch.on", icon:"st.switches.switch.off", backgroundColor:"#ffffff", nextState:"turningOn"
}
}
multiAttributeTile(name:"sliderTile", type:"generic", width:6, height:4) {
tileAttribute("device.switch", key: "PRIMARY_CONTROL") {
attributeState "on", label:'${name}', backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'${name}', backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'${name}', backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute("device.level", key: "SECONDARY_CONTROL") {
attributeState "level", icon: 'st.Weather.weather1', action:"randomizeLevel", defaultState: true
}
tileAttribute("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel", defaultState: true
}
}
multiAttributeTile(name:"valueTile", type:"generic", width:6, height:4) {
tileAttribute("device.level", key: "PRIMARY_CONTROL") {
attributeState "level", label:'${currentValue}', defaultState: true, backgroundColors:[
[value: 0, color: "#ff0000"],
[value: 20, color: "#ffff00"],
[value: 40, color: "#00ff00"],
[value: 60, color: "#00ffff"],
[value: 80, color: "#0000ff"],
[value: 100, color: "#ff00ff"]
]
}
tileAttribute("device.switch", key: "SECONDARY_CONTROL") {
attributeState "on", label:'${name}', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "off", label:'${name}', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
attributeState "turningOn", label:'…', action:"switch.off", icon:"st.switches.switch.on", backgroundColor:"#79b821", nextState:"turningOff"
attributeState "turningOff", label:'…', action:"switch.on", backgroundColor:"#ffffff", nextState:"turningOn"
}
tileAttribute("device.level", key: "VALUE_CONTROL") {
attributeState "VALUE_UP", action: "levelUp"
attributeState "VALUE_DOWN", action: "levelDown"
}
}
multiAttributeTile(name:"lengthyTile", type:"generic", width:6, height:4) {
tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") {
attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", defaultState: true
}
tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") {
attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", defaultState: true
}
}
multiAttributeTile(name:"multilineTile", type:"generic", width:6, height:4) {
tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") {
attributeState "multiLineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", defaultState: true
}
tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") {
attributeState "multiLineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", defaultState: true
}
}
multiAttributeTile(name:"lengthyTileWithIcon", type:"generic", width:6, height:4) {
tileAttribute("device.lengthyText", key: "PRIMARY_CONTROL") {
attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
}
tileAttribute("device.lengthyText", key: "SECONDARY_CONTROL") {
attributeState "lengthyText", label:'The value of this tile is long and should wrap to two lines', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
}
}
multiAttributeTile(name:"multilineTileWithIcon", type:"generic", width:6, height:4) {
tileAttribute("device.multilineText", key: "PRIMARY_CONTROL") {
attributeState "multilineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
}
tileAttribute("device.multilineText", key: "SECONDARY_CONTROL") {
attributeState "multilineText", label:'Line 1 YES\nLine 2 YES\nLine 3 NO', backgroundColor:"#79b821", icon: "st.switches.switch.on", defaultState: true
}
}
main(["basicTile"])
details(["basicTile", "sliderTile", "valueTile", "lengthyTile", "multilineTile", "lengthyTileWithIcon", "multilineTileWithIcon"])
}
}
def installed() {
sendEvent(name: "lengthyText", value: "The value of this tile is long and should wrap to two lines")
sendEvent(name: "multilineText", value: "Line 1 YES\nLine 2 YES\nLine 3 NO")
}
def parse(String description) {
// This is a simulated device. No incoming data to parse.
}
def on() {
log.debug "turningOn"
sendEvent(name: "switch", value: "on")
}
def off() {
log.debug "turningOff"
sendEvent(name: "switch", value: "off")
}
def setLevel(percent) {
log.debug "setLevel: ${percent}, this"
sendEvent(name: "level", value: percent)
}
def randomizeLevel() {
def level = Math.round(Math.random() * 100)
setLevel(level)
}
def levelUp() {
def level = device.latestValue("level") as Integer ?: 0
if (level < 100) {
level = level + 1
}
setLevel(level)
}
def levelDown() {
def level = device.latestValue("level") as Integer ?: 0
if (level > 0) {
level = level - 1
}
setLevel(level)
}

View File

@@ -1,208 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "lightingDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Switch Level"
capability "Actuator"
capability "Color Control"
capability "Power Meter"
capability "Switch"
capability "Refresh"
capability "Sensor"
command "setAdjustedColor"
command "reset"
command "refresh"
}
tiles(scale: 2) {
multiAttributeTile(name:"switch", 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"
}
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'Power level: ${currentValue}W', icon: "st.Appliances.appliances17"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
multiAttributeTile(name:"switchNoPower", 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"
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState "level", action:"switch level.setLevel"
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
multiAttributeTile(name:"switchNoSlider", 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"
}
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'The power level is currently: ${currentValue}W', icon: "st.Appliances.appliances17"
}
tileAttribute ("device.color", key: "COLOR_CONTROL") {
attributeState "color", action:"setAdjustedColor"
}
}
multiAttributeTile(name:"switchNoSliderOrColor", 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"
}
tileAttribute ("device.power", key: "SECONDARY_CONTROL") {
attributeState "power", label:'The light is currently consuming this amount of power: ${currentValue}W', icon: "st.Appliances.appliances17"
}
}
valueTile("color", "device.color", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "color", label: '${currentValue}'
}
standardTile("reset", "device.reset", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "reset", label:"Reset Color", action:"reset", icon:"st.lights.philips.hue-single", defaultState: true
}
standardTile("refresh", "device.switch", inactiveLabel: false, decoration: "flat", width: 2, height: 2) {
state "refresh", label:"", action:"refresh.refresh", icon:"st.secondary.refresh", defaultState: true
}
main(["switch"])
details(["switch", "switchNoPower", "switchNoSlider", "switchNoSliderOrColor", "color", "refresh", "reset"])
}
}
// parse events into attributes
def parse(description) {
log.debug "parse() - $description"
def results = []
def map = description
if (description instanceof String) {
log.debug "Hue Bulb stringToMap - ${map}"
map = stringToMap(description)
}
if (map?.name && map?.value) {
results << createEvent(name: "${map?.name}", value: "${map?.value}")
}
results
}
// handle commands
def on() {
//log.trace parent.on(this)
sendEvent(name: "switch", value: "on")
}
def off() {
//log.trace parent.off(this)
sendEvent(name: "switch", value: "off")
}
def nextLevel() {
def level = device.latestValue("level") as Integer ?: 0
if (level <= 100) {
level = Math.min(25 * (Math.round(level / 25) + 1), 100) as Integer
}
else {
level = 25
}
setLevel(level)
}
def setLevel(percent) {
log.debug "setLevel: ${percent}, this"
sendEvent(name: "level", value: percent)
def power = Math.round(percent / 1.175) * 0.1
sendEvent(name: "power", value: power)
}
def setSaturation(percent) {
log.debug "setSaturation: ${percent}, $this"
sendEvent(name: "saturation", value: percent)
}
def setHue(percent) {
log.debug "setHue: ${percent}, $this"
sendEvent(name: "hue", value: percent)
}
def setColor(value) {
log.debug "setColor: ${value}, $this"
if (value.hue) { sendEvent(name: "hue", value: value.hue)}
if (value.saturation) { sendEvent(name: "saturation", value: value.saturation)}
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)}
}
def reset() {
log.debug "Executing 'reset'"
setAdjustedColor([level:100, hex:"#90C638", saturation:56, hue:23])
}
def setAdjustedColor(value) {
if (value) {
log.trace "setAdjustedColor: ${value}"
def adjusted = value + [:]
adjusted.hue = adjustOutgoingHue(value.hue)
// Needed because color picker always sends 100
adjusted.level = null
setColor(adjusted)
}
}
def refresh() {
log.debug "Executing 'refresh'"
}
def adjustOutgoingHue(percent) {
def adjusted = percent
if (percent > 31) {
if (percent < 63.0) {
adjusted = percent + (7 * (percent -30 ) / 32)
}
else if (percent < 73.0) {
adjusted = 69 + (5 * (percent - 62) / 10)
}
else {
adjusted = percent + (2 * (100 - percent) / 28)
}
}
log.info "percent: $percent, adjusted: $adjusted"
adjusted
}

View File

@@ -1,122 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "mediaPlayerDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Actuator"
capability "Switch"
capability "Refresh"
capability "Sensor"
capability "Music Player"
}
tiles(scale: 2) {
multiAttributeTile(name: "mediaMulti", type:"mediaPlayer", width:6, height:4) {
tileAttribute("device.status", key: "PRIMARY_CONTROL") {
attributeState("paused", label:"Paused",)
attributeState("playing", label:"Playing")
attributeState("stopped", label:"Stopped")
}
tileAttribute("device.status", key: "MEDIA_STATUS") {
attributeState("paused", label:"Paused", action:"music Player.play", nextState: "playing")
attributeState("playing", label:"Playing", action:"music Player.pause", nextState: "paused")
attributeState("stopped", label:"Stopped", action:"music Player.play", nextState: "playing")
}
tileAttribute("device.status", key: "PREVIOUS_TRACK") {
attributeState("status", action:"music Player.previousTrack", defaultState: true)
}
tileAttribute("device.status", key: "NEXT_TRACK") {
attributeState("status", action:"music Player.nextTrack", defaultState: true)
}
tileAttribute ("device.level", key: "SLIDER_CONTROL") {
attributeState("level", action:"music Player.setLevel")
}
tileAttribute ("device.mute", key: "MEDIA_MUTED") {
attributeState("unmuted", action:"music Player.mute", nextState: "muted")
attributeState("muted", action:"music Player.unmute", nextState: "unmuted")
}
tileAttribute("device.trackDescription", key: "MARQUEE") {
attributeState("trackDescription", label:"${currentValue}", defaultState: true)
}
}
main "mediaMulti"
details(["mediaMulti"])
}
}
def installed() {
state.tracks = [
"Gangnam Style (강남스타일)\nPSY\nPsy 6 (Six Rules), Part 1",
"Careless Whisper\nWham!\nMake It Big",
"Never Gonna Give You Up\nRick Astley\nWhenever You Need Somebody",
"Shake It Off\nTaylor Swift\n1989",
"Ironic\nAlanis Morissette\nJagged Little Pill",
"Hotline Bling\nDrake\nHotline Bling - Single"
]
state.currentTrack = 0
sendEvent(name: "level", value: 72)
sendEvent(name: "mute", value: "unmuted")
sendEvent(name: "status", value: "stopped")
}
def parse(description) {
// No parsing will happen with this simulated device.
}
def play() {
sendEvent(name: "status", value: "playing")
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
}
def pause() {
sendEvent(name: "status", value: "paused")
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
}
def stop() {
sendEvent(name: "status", value: "stopped")
}
def previousTrack() {
state.currentTrack = state.currentTrack - 1
if (state.currentTrack < 0)
state.currentTrack = state.tracks.size()-1
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
}
def nextTrack() {
state.currentTrack = state.currentTrack + 1
if (state.currentTrack == state.tracks.size())
state.currentTrack = 0
sendEvent(name: "trackDescription", value: state.tracks[state.currentTrack])
}
def mute() {
sendEvent(name: "mute", value: "muted")
}
def unmute() {
sendEvent(name: "mute", value: "unmuted")
}
def setLevel(level) {
sendEvent(name: "level", value: level)
}

View File

@@ -1,343 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "thermostatDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Thermostat"
capability "Relative Humidity Measurement"
command "tempUp"
command "tempDown"
command "heatUp"
command "heatDown"
command "coolUp"
command "coolDown"
command "setTemperature", ["number"]
}
tiles(scale: 2) {
multiAttributeTile(name:"thermostatFull", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("temp", label:'${currentValue}', unit:"dF", defaultState: true)
}
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "tempUp")
attributeState("VALUE_DOWN", action: "tempDown")
}
tileAttribute("device.humidity", key: "SECONDARY_CONTROL") {
attributeState("humidity", label:'${currentValue}%', unit:"%", defaultState: true)
}
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
attributeState("idle", backgroundColor:"#44b621")
attributeState("heating", backgroundColor:"#ffa81e")
attributeState("cooling", backgroundColor:"#269bd2")
}
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
attributeState("off", label:'${name}')
attributeState("heat", label:'${name}')
attributeState("cool", label:'${name}')
attributeState("auto", label:'${name}')
}
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("heatingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
}
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
}
}
multiAttributeTile(name:"thermostatNoHumidity", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
attributeState("temp", label:'${currentValue}', unit:"dF")
}
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "tempUp")
attributeState("VALUE_DOWN", action: "tempDown")
}
tileAttribute("device.thermostatOperatingState", key: "OPERATING_STATE") {
attributeState("idle", backgroundColor:"#44b621")
attributeState("heating", backgroundColor:"#ffa81e")
attributeState("cooling", backgroundColor:"#269bd2")
}
tileAttribute("device.thermostatMode", key: "THERMOSTAT_MODE") {
attributeState("off", label:'${name}')
attributeState("heat", label:'${name}')
attributeState("cool", label:'${name}')
attributeState("auto", label:'${name}')
}
tileAttribute("device.heatingSetpoint", key: "HEATING_SETPOINT") {
attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
attributeState("heatingSetpoint", label:'${currentValue}', unit:"dF")
}
tileAttribute("device.coolingSetpoint", key: "COOLING_SETPOINT") {
attributeState("coolingSetpoint", label:'${currentValue}', unit:"dF", defaultState: true)
}
}
multiAttributeTile(name:"thermostatBasic", type:"thermostat", width:6, height:4) {
tileAttribute("device.temperature", key: "PRIMARY_CONTROL") {
attributeState("temp", label:'${currentValue}', unit:"dF", defaultState: true,
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
])
}
tileAttribute("device.temperature", key: "VALUE_CONTROL") {
attributeState("VALUE_UP", action: "tempUp")
attributeState("VALUE_DOWN", action: "tempDown")
}
}
valueTile("temperature", "device.temperature", width: 2, height: 2) {
state("temperature", label:'${currentValue}', unit:"dF",
backgroundColors:[
[value: 31, color: "#153591"],
[value: 44, color: "#1e9cbb"],
[value: 59, color: "#90d2a7"],
[value: 74, color: "#44b621"],
[value: 84, color: "#f1d801"],
[value: 95, color: "#d04e00"],
[value: 96, color: "#bc2323"]
]
)
}
standardTile("tempDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "tempDown", label:'down', action:"tempDown", defaultState: true
}
standardTile("tempUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "tempUp", label:'up', action:"tempUp", defaultState: true
}
valueTile("heatingSetpoint", "device.heatingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "heat", label:'${currentValue} heat', unit: "F", backgroundColor:"#ffffff"
}
standardTile("heatDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "heatDown", label:'down', action:"heatDown", defaultState: true
}
standardTile("heatUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "heatUp", label:'up', action:"heatUp", defaultState: true
}
valueTile("coolingSetpoint", "device.coolingSetpoint", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "cool", label:'${currentValue} cool', unit:"F", backgroundColor:"#ffffff"
}
standardTile("coolDown", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "coolDown", label:'down', action:"coolDown", defaultState: true
}
standardTile("coolUp", "device.temperature", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "coolUp", label:'up', action:"coolUp", defaultState: true
}
standardTile("mode", "device.thermostatMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "off", label:'${name}', action:"thermostat.heat", backgroundColor:"#ffffff"
state "heat", label:'${name}', action:"thermostat.cool", backgroundColor:"#ffa81e"
state "cool", label:'${name}', action:"thermostat.auto", backgroundColor:"#269bd2"
state "auto", label:'${name}', action:"thermostat.off", backgroundColor:"#79b821"
}
standardTile("fanMode", "device.thermostatFanMode", width: 2, height: 2, inactiveLabel: false, decoration: "flat") {
state "fanAuto", label:'${name}', action:"thermostat.fanOn", backgroundColor:"#ffffff"
state "fanOn", label:'${name}', action:"thermostat.fanCirculate", backgroundColor:"#ffffff"
state "fanCirculate", label:'${name}', action:"thermostat.fanAuto", backgroundColor:"#ffffff"
}
standardTile("operatingState", "device.thermostatOperatingState", width: 2, height: 2) {
state "idle", label:'${name}', backgroundColor:"#ffffff"
state "heating", label:'${name}', backgroundColor:"#ffa81e"
state "cooling", label:'${name}', backgroundColor:"#269bd2"
}
main("thermostatFull")
details([
"thermostatFull", "thermostatNoHumidity", "thermostatBasic",
"temperature","tempDown","tempUp",
"mode", "fanMode", "operatingState",
"heatingSetpoint", "heatDown", "heatUp",
"coolingSetpoint", "coolDown", "coolUp"
])
}
}
def installed() {
sendEvent(name: "temperature", value: 72, unit: "F")
sendEvent(name: "heatingSetpoint", value: 70, unit: "F")
sendEvent(name: "thermostatSetpoint", value: 70, unit: "F")
sendEvent(name: "coolingSetpoint", value: 76, unit: "F")
sendEvent(name: "thermostatMode", value: "off")
sendEvent(name: "thermostatFanMode", value: "fanAuto")
sendEvent(name: "thermostatOperatingState", value: "idle")
sendEvent(name: "humidity", value: 53, unit: "%")
}
def parse(String description) {
}
def evaluate(temp, heatingSetpoint, coolingSetpoint) {
log.debug "evaluate($temp, $heatingSetpoint, $coolingSetpoint"
def threshold = 1.0
def current = device.currentValue("thermostatOperatingState")
def mode = device.currentValue("thermostatMode")
def heating = false
def cooling = false
def idle = false
if (mode in ["heat","emergency heat","auto"]) {
if (heatingSetpoint - temp >= threshold) {
heating = true
sendEvent(name: "thermostatOperatingState", value: "heating")
}
else if (temp - heatingSetpoint >= threshold) {
idle = true
}
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
}
if (mode in ["cool","auto"]) {
if (temp - coolingSetpoint >= threshold) {
cooling = true
sendEvent(name: "thermostatOperatingState", value: "cooling")
}
else if (coolingSetpoint - temp >= threshold && !heating) {
idle = true
}
sendEvent(name: "thermostatSetpoint", value: coolingSetpoint)
}
else {
sendEvent(name: "thermostatSetpoint", value: heatingSetpoint)
}
if (mode == "off") {
idle = true
}
if (idle && !heating && !cooling) {
sendEvent(name: "thermostatOperatingState", value: "idle")
}
}
def setHeatingSetpoint(Double degreesF) {
log.debug "setHeatingSetpoint($degreesF)"
sendEvent(name: "heatingSetpoint", value: degreesF)
evaluate(device.currentValue("temperature"), degreesF, device.currentValue("coolingSetpoint"))
}
def setCoolingSetpoint(Double degreesF) {
log.debug "setCoolingSetpoint($degreesF)"
sendEvent(name: "coolingSetpoint", value: degreesF)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), degreesF)
}
def setThermostatMode(String value) {
sendEvent(name: "thermostatMode", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def setThermostatFanMode(String value) {
sendEvent(name: "thermostatFanMode", value: value)
}
def off() {
sendEvent(name: "thermostatMode", value: "off")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def heat() {
sendEvent(name: "thermostatMode", value: "heat")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def auto() {
sendEvent(name: "thermostatMode", value: "auto")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def emergencyHeat() {
sendEvent(name: "thermostatMode", value: "emergency heat")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def cool() {
sendEvent(name: "thermostatMode", value: "cool")
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def fanOn() {
sendEvent(name: "thermostatFanMode", value: "fanOn")
}
def fanAuto() {
sendEvent(name: "thermostatFanMode", value: "fanAuto")
}
def fanCirculate() {
sendEvent(name: "thermostatFanMode", value: "fanCirculate")
}
def poll() {
null
}
def tempUp() {
def ts = device.currentState("temperature")
def value = ts ? ts.integerValue + 1 : 72
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def tempDown() {
def ts = device.currentState("temperature")
def value = ts ? ts.integerValue - 1 : 72
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def setTemperature(value) {
def ts = device.currentState("temperature")
sendEvent(name:"temperature", value: value)
evaluate(value, device.currentValue("heatingSetpoint"), device.currentValue("coolingSetpoint"))
}
def heatUp() {
def ts = device.currentState("heatingSetpoint")
def value = ts ? ts.integerValue + 1 : 68
sendEvent(name:"heatingSetpoint", value: value)
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
}
def heatDown() {
def ts = device.currentState("heatingSetpoint")
def value = ts ? ts.integerValue - 1 : 68
sendEvent(name:"heatingSetpoint", value: value)
evaluate(device.currentValue("temperature"), value, device.currentValue("coolingSetpoint"))
}
def coolUp() {
def ts = device.currentState("coolingSetpoint")
def value = ts ? ts.integerValue + 1 : 76
sendEvent(name:"coolingSetpoint", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
}
def coolDown() {
def ts = device.currentState("coolingSetpoint")
def value = ts ? ts.integerValue - 1 : 76
sendEvent(name:"coolingSetpoint", value: value)
evaluate(device.currentValue("temperature"), device.currentValue("heatingSetpoint"), value)
}

View File

@@ -1,169 +0,0 @@
/**
* Copyright 2016 SmartThings, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (
name: "videoPlayerDeviceTile",
namespace: "smartthings/tile-ux",
author: "SmartThings") {
capability "Configuration"
capability "Video Camera"
capability "Video Capture"
capability "Refresh"
capability "Switch"
// custom commands
command "start"
command "stop"
command "setProfileHD"
command "setProfileSDH"
command "setProfileSDL"
}
tiles(scale: 2) {
multiAttributeTile(name: "videoPlayer", type: "videoPlayer", width: 6, height: 4) {
tileAttribute("device.switch", key: "CAMERA_STATUS") {
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", action: "switch.off", backgroundColor: "#79b821", defaultState: true)
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", action: "switch.on", backgroundColor: "#ffffff")
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", action: "refresh.refresh", backgroundColor: "#F22000")
}
tileAttribute("device.errorMessage", key: "CAMERA_ERROR_MESSAGE") {
attributeState("errorMessage", label: "", value: "", defaultState: true)
}
tileAttribute("device.camera", key: "PRIMARY_CONTROL") {
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", backgroundColor: "#79b821", defaultState: true)
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", backgroundColor: "#ffffff")
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", backgroundColor: "#F22000")
}
tileAttribute("device.startLive", key: "START_LIVE") {
attributeState("live", action: "start", defaultState: true)
}
tileAttribute("device.stream", key: "STREAM_URL") {
attributeState("activeURL", defaultState: true)
}
tileAttribute("device.profile", key: "STREAM_QUALITY") {
attributeState("1", label: "720p", action: "setProfileHD", defaultState: true)
attributeState("2", label: "h360p", action: "setProfileSDH", defaultState: true)
attributeState("3", label: "l360p", action: "setProfileSDL", defaultState: true)
}
tileAttribute("device.betaLogo", key: "BETA_LOGO") {
attributeState("betaLogo", label: "", value: "", defaultState: true)
}
}
multiAttributeTile(name: "videoPlayerMin", type: "videoPlayer", width: 6, height: 4) {
tileAttribute("device.switch", key: "CAMERA_STATUS") {
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", action: "switch.off", backgroundColor: "#79b821", defaultState: true)
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", action: "switch.on", backgroundColor: "#ffffff")
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", action: "refresh.refresh", backgroundColor: "#F22000")
}
tileAttribute("device.errorMessage", key: "CAMERA_ERROR_MESSAGE") {
attributeState("errorMessage", label: "", value: "", defaultState: true)
}
tileAttribute("device.camera", key: "PRIMARY_CONTROL") {
attributeState("on", label: "Active", icon: "st.camera.dlink-indoor", backgroundColor: "#79b821", defaultState: true)
attributeState("off", label: "Inactive", icon: "st.camera.dlink-indoor", backgroundColor: "#ffffff")
attributeState("restarting", label: "Connecting", icon: "st.camera.dlink-indoor", backgroundColor: "#53a7c0")
attributeState("unavailable", label: "Unavailable", icon: "st.camera.dlink-indoor", backgroundColor: "#F22000")
}
tileAttribute("device.startLive", key: "START_LIVE") {
attributeState("live", action: "start", defaultState: true)
}
tileAttribute("device.stream", key: "STREAM_URL") {
attributeState("activeURL", defaultState: true)
}
}
main("videoPlayer")
details([
"videoPlayer", "videoPlayerMin"
])
}
}
def installed() {
}
def parse(String description) {
}
def refresh() {
log.trace "refresh()"
// no-op
}
def on() {
log.trace "on()"
// no-op
}
def off() {
log.trace "off()"
// no-op
}
def setProfile(profile) {
log.trace "setProfile(): ${profile}"
sendEvent(name: "profile", value: profile, displayed: false)
}
def setProfileHD() {
setProfile(1)
}
def setProfileSDH() {
setProfile(2)
}
def setProfileSDL() {
setProfile(3)
}
def start() {
log.trace "start()"
def dataLiveVideo = [
OutHomeURL : "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
InHomeURL : "https://devimages.apple.com.edgekey.net/streaming/examples/bipbop_4x3/bipbop_4x3_variant.m3u8",
ThumbnailURL: "http://cdn.device-icons.smartthings.com/camera/dlink-indoor@2x.png",
cookie : [key: "key", value: "value"]
]
def event = [
name : "stream",
value : groovy.json.JsonOutput.toJson(dataLiveVideo).toString(),
data : groovy.json.JsonOutput.toJson(dataLiveVideo),
descriptionText: "Starting the livestream",
eventType : "VIDEO",
displayed : false,
isStateChange : true
]
sendEvent(event)
}
def stop() {
log.trace "stop()"
}

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

View File

@@ -1,42 +0,0 @@
# Tyco Door Window Sensor
Works with:
* [Tyco Door Window Sensor](https://support.smartthings.com/hc/en-us/articles/204834100-Tyco-Door-Window-Sensor)
## Table of contents
* [Capabilities](#capabilities)
* [Health](#device-health)
* [Battery](#battery-specification)
## Capabilities
* **Battery** - defines device uses a battery
* **Configuration** - _configure()_ command called when device is installed or device preferences updated
* **Contact Sensor** - can detect contact (open/close)
* **Refresh** - _refresh()_ command for status updates
* **Temperature Measurement** - can measure the device temperature
* **Health Check** - indicates ability to get device health notifications
## Device Health
Contact sensor with maxReportTime of 5 mins.
Check-in interval is double the value of maxReportTime for Zigbee device.
This gives the device twice the amount of time to respond before it is marked as offline.
Check-in interval = 12 min
## Battery Specification
3V CR2032 battery is required.
## Troubleshooting
If the device doesn't pair when trying from the SmartThings mobile app, it is possible that either the sensor needs to be reseted or the sensor is out of range.
Reset needs to be done by inserting the battery in the sensor and then quickly pressing the adjacent black button 10 times. Pairing should be tried again now.
It may happen that sensor is out of range, then pairing needs to be tried again by placing the sensor closer to the hub.
Instructions related to pairing, resetting and removing the different motion sensors from SmartThings can be found in the following links
for the different models:
* [Tyco Door Window Sensor (MCT-340)](https://support.smartthings.com/hc/en-us/articles/204834100-Tyco-Door-Window-Sensor)

View File

@@ -13,7 +13,6 @@
* for the specific language governing permissions and limitations under the License. * for the specific language governing permissions and limitations under the License.
* *
*/ */
import physicalgraph.zigbee.clusters.iaszone.ZoneStatus
metadata { metadata {
definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") { definition (name: "Tyco Door/Window Sensor", namespace: "smartthings", author: "SmartThings") {
@@ -22,7 +21,6 @@ metadata {
capability "Contact Sensor" capability "Contact Sensor"
capability "Refresh" capability "Refresh"
capability "Temperature Measurement" capability "Temperature Measurement"
capability "Health Check"
command "enrollResponse" command "enrollResponse"
@@ -163,9 +161,40 @@ private Map parseCustomMessage(String description) {
} }
private Map parseIasMessage(String description) { private Map parseIasMessage(String description) {
ZoneStatus zs = zigbee.parseZoneStatus(description) List parsedMsg = description.split(' ')
String msgCode = parsedMsg[2]
return zs.isAlarm1Set() ? getContactResult('open') : getContactResult('closed') Map resultMap = [:]
switch(msgCode) {
case '0x0020': // Closed/No Motion/Dry
resultMap = getContactResult('closed')
break
case '0x0021': // Open/Motion/Wet
resultMap = getContactResult('open')
break
case '0x0022': // Tamper Alarm
break
case '0x0023': // Battery Alarm
break
case '0x0024': // Supervision Report
resultMap = getContactResult('closed')
break
case '0x0025': // Restore Report
resultMap = getContactResult('open')
break
case '0x0026': // Trouble/Failure
break
case '0x0028': // Test Mode
break
}
return resultMap
} }
def getTemperature(value) { def getTemperature(value) {
@@ -194,8 +223,7 @@ private Map getBatteryResult(rawValue) {
def minVolts = 2.1 def minVolts = 2.1
def maxVolts = 3.0 def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts) def pct = (volts - minVolts) / (maxVolts - minVolts)
def roundedPct = Math.round(pct * 100) result.value = Math.min(100, (int) pct * 100)
result.value = Math.min(100, roundedPct)
result.descriptionText = "${linkText} battery was ${result.value}%" result.descriptionText = "${linkText} battery was ${result.value}%"
} }
@@ -214,8 +242,7 @@ private Map getTemperatureResult(value) {
return [ return [
name: 'temperature', name: 'temperature',
value: value, value: value,
descriptionText: descriptionText, descriptionText: descriptionText
unit: temperatureScale
] ]
} }
@@ -230,42 +257,44 @@ private Map getContactResult(value) {
] ]
} }
/**
* PING is used by Device-Watch in attempt to reach the Device
* */
def ping() {
return zigbee.readAttribute(0x0402, 0x0000) // Read the Temperature Cluster
}
def refresh() def refresh()
{ {
log.debug "Refreshing Temperature and Battery" 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" "st rattr 0x${device.deviceNetworkId} 1 1 0x20"
] ]
return refreshCmds + enrollResponse()
} }
def configure() { def configure() {
// Device-Watch allows 2 check-in misses from device
sendEvent(name: "checkInterval", value: 60 * 12, displayed: false, data: [protocol: "zigbee", hubHardwareId: device.hub.hardwareID])
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 enrollCmds = [ def configCmds = [
"delay 1000", "delay 1000",
"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 1500", "send 0x${device.deviceNetworkId} 1 1", "delay 1500",
"zcl global send-me-a-report 1 0x20 0x20 600 3600 {01}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
"zcl global send-me-a-report 0x402 0 0x29 300 3600 {6400}", "delay 200",
"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
//"raw 0x500 {01 23 00 00 00}", "delay 200", //"raw 0x500 {01 23 00 00 00}", "delay 200",
//"send 0x${device.deviceNetworkId} 1 1", "delay 1500", //"send 0x${device.deviceNetworkId} 1 1", "delay 1500",
"zdo bind 0x${device.deviceNetworkId} 1 1 0x402 {${device.zigbeeId}} {}", "delay 500",
"zdo bind 0x${device.deviceNetworkId} 1 1 1 {${device.zigbeeId}} {}",
"delay 500"
] ]
return enrollCmds + zigbee.batteryConfig() + zigbee.temperatureConfig(30, 300) + refresh() // send refresh cmds as part of config return configCmds + enrollResponse() + refresh() // send refresh cmds as part of config
} }
def enrollResponse() { def enrollResponse() {

View File

@@ -33,7 +33,7 @@ metadata {
state "power", label: '${currentValue} W' state "power", label: '${currentValue} W'
} }
htmlTile(name: "powerContent", attribute: "powerContent", type: "HTML", whitelist: ["www.wattvision.com"] , url: '${currentValue}', width: 3, height: 2) htmlTile(name: "powerContent", attribute: "powerContent", type: "HTML", whitelist: "www.wattvision.com" , url: '${currentValue}', width: 3, height: 2)
standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") { standardTile("refresh", "device.power", inactiveLabel: false, decoration: "flat") {
state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh" state "default", label: '', action: "refresh.refresh", icon: "st.secondary.refresh"

View File

@@ -1,241 +0,0 @@
/**
* ZigBee Button
*
* Copyright 2015 Mitch Pond
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
* in compliance with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License
* for the specific language governing permissions and limitations under the License.
*
*/
metadata {
definition (name: "ZigBee Button", namespace: "smartthings", author: "Mitch Pond") {
capability "Actuator"
capability "Battery"
capability "Button"
capability "Configuration"
capability "Refresh"
capability "Sensor"
command "enrollResponse"
fingerprint inClusters: "0000, 0001, 0003, 0020, 0402, 0B05", outClusters: "0003, 0006, 0008, 0019", manufacturer: "OSRAM", model: "LIGHTIFY Dimming Switch", deviceJoinName: "OSRAM LIGHTIFY Dimming Switch"
//fingerprint inClusters: "0000, 0001, 0003, 0020, 0500", outClusters: "0003,0019", manufacturer: "CentraLite", model: "3455-L", deviceJoinName: "Iris Care Pendant"
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0402, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model: "3460-L", deviceJoinName: "Iris Smart Button"
//fingerprint inClusters: "0000, 0001, 0003, 0007, 0020, 0B05", outClusters: "0003, 0006, 0019", manufacturer: "CentraLite", model:"3450-L", deviceJoinName: "Iris KeyFob"
}
simulator {}
preferences {
section {
input ("holdTime", "number", title: "Minimum time in seconds for a press to count as \"held\"", defaultValue: 1, displayDuringSetup: false)
}
}
tiles {
standardTile("button", "device.button", width: 2, height: 2) {
state "default", label: "", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#ffffff"
state "button 1 pushed", label: "pushed #1", icon: "st.unknown.zwave.remote-controller", backgroundColor: "#79b821"
}
valueTile("battery", "device.battery", decoration: "flat", inactiveLabel: false) {
state "battery", label:'${currentValue}% battery', unit:""
}
standardTile("refresh", "device.refresh", inactiveLabel: false, decoration: "flat") {
state "default", action:"refresh.refresh", icon:"st.secondary.refresh"
}
main (["button"])
details(["button", "battery", "refresh"])
}
}
def parse(String description) {
log.debug "description is $description"
def event = zigbee.getEvent(description)
if (event) {
sendEvent(event)
}
else {
if ((description?.startsWith("catchall:")) || (description?.startsWith("read attr -"))) {
def descMap = zigbee.parseDescriptionAsMap(description)
if (descMap.clusterInt == 0x0001 && descMap.attrInt == 0x0020) {
event = getBatteryResult(zigbee.convertHexToInt(descMap.value))
}
else if (descMap.clusterInt == 0x0006 || descMap.clusterInt == 0x0008) {
event = parseNonIasButtonMessage(descMap)
}
}
else if (description?.startsWith('zone status')) {
event = parseIasButtonMessage(description)
}
log.debug "Parse returned $event"
def result = event ? createEvent(event) : []
if (description?.startsWith('enroll request')) {
List cmds = enrollResponse()
result = cmds?.collect { new physicalgraph.device.HubAction(it) }
}
return result
}
}
private Map parseIasButtonMessage(String description) {
def zs = zigbee.parseZoneStatus(description)
return zs.isAlarm2Set() ? getButtonResult("press") : getButtonResult("release")
}
private Map getBatteryResult(rawValue) {
log.debug 'Battery'
def volts = rawValue / 10
if (volts > 3.0 || volts == 0 || rawValue == 0xFF) {
return [:]
}
else {
def result = [
name: 'battery'
]
def minVolts = 2.1
def maxVolts = 3.0
def pct = (volts - minVolts) / (maxVolts - minVolts)
result.value = Math.min(100, (int) pct * 100)
def linkText = getLinkText(device)
result.descriptionText = "${linkText} battery was ${result.value}%"
return result
}
}
private Map parseNonIasButtonMessage(Map descMap){
def buttonState = ""
def buttonNumber = 0
if (((device.getDataValue("model") == "3460-L") || (device.getDataValue("model") == "3450-L"))
&&(descMap.clusterInt == 0x0006)) {
if (descMap.command == "01") {
getButtonResult("press")
}
else if (descMap.command == "00") {
getButtonResult("release")
}
}
else if (descMap.clusterInt == 0x0006) {
buttonState = "pushed"
if (descMap.command == "01") {
buttonNumber = 1
}
else if (descMap.command == "00") {
buttonNumber = 2
}
if (buttonNumber !=0) {
def descriptionText = "$device.displayName button $buttonNumber was $buttonState"
return createEvent(name: "button", value: buttonState, data: [buttonNumber: buttonNumber], descriptionText: descriptionText, isStateChange: true)
}
else {
return [:]
}
}
else if (descMap.clusterInt == 0x0008) {
if (descMap.command == "05") {
state.buttonNumber = 1
getButtonResult("press", 1)
}
else if (descMap.command == "01") {
state.buttonNumber = 2
getButtonResult("press", 2)
}
else if (descMap.command == "03") {
getButtonResult("release", state.buttonNumber)
}
}
}
def refresh() {
log.debug "Refreshing Battery"
return zigbee.readAttribute(0x0001, 0x20) +
zigbee.enrollResponse()
}
def configure() {
log.debug "Configuring Reporting, IAS CIE, and Bindings."
def cmds = []
if (device.getDataValue("model") == "3450-L") {
cmds << [
"zdo bind 0x${device.deviceNetworkId} 1 1 6 {${device.zigbeeId}} {}", "delay 300",
"zdo bind 0x${device.deviceNetworkId} 2 1 6 {${device.zigbeeId}} {}", "delay 300",
"zdo bind 0x${device.deviceNetworkId} 3 1 6 {${device.zigbeeId}} {}", "delay 300",
"zdo bind 0x${device.deviceNetworkId} 4 1 6 {${device.zigbeeId}} {}", "delay 300"
]
}
return zigbee.onOffConfig() +
zigbee.levelConfig() +
zigbee.configureReporting(0x0001, 0x20, 0x20, 30, 21600, 0x01) +
zigbee.enrollResponse() +
zigbee.readAttribute(0x0001, 0x20) +
cmds
}
private Map getButtonResult(buttonState, buttonNumber = 1) {
if (buttonState == 'release') {
log.debug "Button was value : $buttonState"
def timeDiff = now() - state.pressTime
log.info "timeDiff: $timeDiff"
def holdPreference = holdTime ?: 1
log.info "holdp1 : $holdPreference"
holdPreference = (holdPreference as int) * 1000
log.info "holdp2 : $holdPreference"
if (timeDiff > 10000) { //timeDiff>10sec check for refresh sending release value causing actions to be executed
return [:]
}
else {
if (timeDiff < holdPreference) {
buttonState = "pushed"
}
else {
buttonState = "held"
}
def descriptionText = "$device.displayName button $buttonNumber was $buttonState"
return createEvent(name: "button", value: buttonState, data: [buttonNumber: buttonNumber], descriptionText: descriptionText, isStateChange: true)
}
}
else if (buttonState == 'press') {
log.debug "Button was value : $buttonState"
state.pressTime = now()
log.info "presstime: ${state.pressTime}"
return [:]
}
}
def installed() {
initialize()
}
def updated() {
initialize()
}
def initialize() {
if ((device.getDataValue("manufacturer") == "OSRAM") && (device.getDataValue("model") == "LIGHTIFY Dimming Switch")) {
sendEvent(name: "numberOfButtons", value: 2)
}
else if ((device.getDataValue("manufacturer") == "CentraLite") &&
((device.getDataValue("model") == "3455-L") || (device.getDataValue("model") == "3460-L"))) {
sendEvent(name: "numberOfButtons", value: 1)
}
else if ((device.getDataValue("manufacturer") == "CentraLite") && (device.getDataValue("model") == "3450-L")) {
sendEvent(name: "numberOfButtons", value: 4)
}
else {
//default. can be changed
sendEvent(name: "numberOfButtons", value: 4)
}
}

View File

@@ -56,17 +56,21 @@ metadata {
def parse(String description) { def parse(String description) {
log.debug "description is $description" log.debug "description is $description"
def event = zigbee.getEvent(description) def resultMap = zigbee.getKnownDescription(description)
if (event) { if (resultMap) {
log.info event log.info resultMap
if (event.name == "power") { if (resultMap.type == "update") {
log.info "$device updates: ${resultMap.value}"
}
else if (resultMap.type == "power") {
def powerValue
if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power if (device.getDataValue("manufacturer") != "OSRAM") { //OSRAM devices do not reliably update power
event.value = (event.value as Integer) / 10 //TODO: The divisor value needs to be set as part of configuration powerValue = (resultMap.value as Integer)/10 //TODO: The divisor value needs to be set as part of configuration
sendEvent(event) sendEvent(name: "power", value: powerValue)
} }
} }
else { else {
sendEvent(event) sendEvent(name: resultMap.type, value: resultMap.value)
} }
} }
else { else {
@@ -93,5 +97,5 @@ def refresh() {
def configure() { def configure() {
log.debug "Configuring Reporting and Bindings." log.debug "Configuring Reporting and Bindings."
refresh() zigbee.onOffConfig() + zigbee.levelConfig() + zigbee.simpleMeteringPowerConfig() + zigbee.electricMeasurementPowerConfig() + zigbee.onOffRefresh() + zigbee.levelRefresh() + zigbee.simpleMeteringPowerRefresh() + zigbee.electricMeasurementPowerRefresh()
} }

View File

@@ -1,2 +0,0 @@
.st-ignore
README.md

Some files were not shown because too many files have changed in this diff Show More